use std::sync::Mutex;
static TEST_MUTEX: Mutex<()> = Mutex::new(());
use std::ffi::{CStr, c_char};
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use vk::Handle;
use vulkan_rust::vk;
static MOCK_CALLED: AtomicBool = AtomicBool::new(false);
static MOCK_VERTEX_COUNT: AtomicU32 = AtomicU32::new(0);
static MOCK_INSTANCE_COUNT: AtomicU32 = AtomicU32::new(0);
fn reset_mocks() {
MOCK_CALLED.store(false, Ordering::SeqCst);
MOCK_VERTEX_COUNT.store(0, Ordering::SeqCst);
MOCK_INSTANCE_COUNT.store(0, Ordering::SeqCst);
}
unsafe extern "system" fn mock_device_proc_addr(
_device: vk::Device,
name: *const c_char,
) -> vk::PFN_vkVoidFunction {
let name = unsafe { CStr::from_ptr(name) };
match name.to_bytes() {
b"vkCreateFence" => Some(unsafe {
std::mem::transmute::<
unsafe extern "system" fn(
vk::Device,
*const vk::FenceCreateInfo,
*const vk::AllocationCallbacks,
*mut vk::Fence,
) -> vk::Result,
unsafe extern "system" fn(),
>(mock_create_fence)
}),
b"vkDestroyFence" => Some(unsafe {
std::mem::transmute::<
unsafe extern "system" fn(vk::Device, vk::Fence, *const vk::AllocationCallbacks),
unsafe extern "system" fn(),
>(mock_destroy_fence)
}),
b"vkDeviceWaitIdle" => Some(unsafe {
std::mem::transmute::<
unsafe extern "system" fn(vk::Device) -> vk::Result,
unsafe extern "system" fn(),
>(mock_device_wait_idle)
}),
b"vkCmdDraw" => Some(unsafe {
std::mem::transmute::<
unsafe extern "system" fn(vk::CommandBuffer, u32, u32, u32, u32),
unsafe extern "system" fn(),
>(mock_cmd_draw)
}),
_ => None,
}
}
fn mock_device() -> vulkan_rust::Device {
unsafe {
vulkan_rust::Device::from_raw_parts(
vk::Device::from_raw(0xD001),
Some(mock_device_proc_addr),
)
}
}
unsafe extern "system" fn mock_create_fence(
device: vk::Device,
p_create_info: *const vk::FenceCreateInfo,
_p_allocator: *const vk::AllocationCallbacks,
p_fence: *mut vk::Fence,
) -> vk::Result {
MOCK_CALLED.store(true, Ordering::SeqCst);
assert_eq!(device.as_raw(), 0xD001, "wrong device handle");
assert!(!p_create_info.is_null(), "create_info must not be null");
assert!(!p_fence.is_null(), "output pointer must not be null");
unsafe { *p_fence = vk::Fence::from_raw(0xFE01) };
vk::Result::SUCCESS
}
unsafe extern "system" fn mock_destroy_fence(
device: vk::Device,
fence: vk::Fence,
p_allocator: *const vk::AllocationCallbacks,
) {
MOCK_CALLED.store(true, Ordering::SeqCst);
assert_eq!(device.as_raw(), 0xD001, "wrong device handle");
assert_eq!(fence.as_raw(), 0xFE01, "wrong fence handle");
assert!(p_allocator.is_null(), "allocator should be null when None");
}
unsafe extern "system" fn mock_device_wait_idle(device: vk::Device) -> vk::Result {
MOCK_CALLED.store(true, Ordering::SeqCst);
assert_eq!(device.as_raw(), 0xD001, "wrong device handle");
vk::Result::SUCCESS
}
unsafe extern "system" fn mock_cmd_draw(
_command_buffer: vk::CommandBuffer,
vertex_count: u32,
instance_count: u32,
_first_vertex: u32,
_first_instance: u32,
) {
MOCK_CALLED.store(true, Ordering::SeqCst);
MOCK_VERTEX_COUNT.store(vertex_count, Ordering::SeqCst);
MOCK_INSTANCE_COUNT.store(instance_count, Ordering::SeqCst);
}
unsafe extern "system" fn mock_instance_proc_addr(
_instance: vk::Instance,
name: *const c_char,
) -> vk::PFN_vkVoidFunction {
let name = unsafe { CStr::from_ptr(name) };
match name.to_bytes() {
b"vkGetDeviceProcAddr" => Some(unsafe {
std::mem::transmute::<
unsafe extern "system" fn(vk::Device, *const c_char) -> vk::PFN_vkVoidFunction,
unsafe extern "system" fn(),
>(null_device_proc_addr)
}),
b"vkEnumeratePhysicalDevices" => Some(unsafe {
std::mem::transmute::<
unsafe extern "system" fn(
vk::Instance,
*mut u32,
*mut vk::PhysicalDevice,
) -> vk::Result,
unsafe extern "system" fn(),
>(mock_enumerate_physical_devices)
}),
b"vkGetPhysicalDeviceQueueFamilyProperties" => Some(unsafe {
std::mem::transmute::<
unsafe extern "system" fn(
vk::PhysicalDevice,
*mut u32,
*mut vk::QueueFamilyProperties,
),
unsafe extern "system" fn(),
>(mock_get_physical_device_queue_family_properties)
}),
b"vkGetPhysicalDeviceMemoryProperties" => Some(unsafe {
std::mem::transmute::<
unsafe extern "system" fn(
vk::PhysicalDevice,
*mut vk::PhysicalDeviceMemoryProperties,
),
unsafe extern "system" fn(),
>(mock_get_physical_device_memory_properties)
}),
_ => None,
}
}
unsafe extern "system" fn null_device_proc_addr(
_device: vk::Device,
_name: *const c_char,
) -> vk::PFN_vkVoidFunction {
None
}
fn mock_instance() -> vulkan_rust::Instance {
unsafe {
vulkan_rust::Instance::from_raw_parts(
vk::Instance::from_raw(0x1001_usize),
Some(mock_instance_proc_addr),
)
}
}
unsafe extern "system" fn mock_enumerate_physical_devices(
_instance: vk::Instance,
p_count: *mut u32,
p_devices: *mut vk::PhysicalDevice,
) -> vk::Result {
MOCK_CALLED.store(true, Ordering::SeqCst);
if p_devices.is_null() {
unsafe { *p_count = 2 };
} else {
unsafe {
*p_count = 2;
*p_devices = vk::PhysicalDevice::from_raw(0xAA01_usize);
*p_devices.add(1) = vk::PhysicalDevice::from_raw(0xAA02_usize);
}
}
vk::Result::SUCCESS
}
unsafe extern "system" fn mock_get_physical_device_queue_family_properties(
_physical_device: vk::PhysicalDevice,
p_count: *mut u32,
p_properties: *mut vk::QueueFamilyProperties,
) {
MOCK_CALLED.store(true, Ordering::SeqCst);
if p_properties.is_null() {
unsafe { *p_count = 1 };
} else {
unsafe {
*p_count = 1;
let mut props: vk::QueueFamilyProperties = std::mem::zeroed();
props.queue_flags = vk::QueueFlagBits::GRAPHICS;
props.queue_count = 4;
*p_properties = props;
}
}
}
unsafe extern "system" fn mock_get_physical_device_memory_properties(
_physical_device: vk::PhysicalDevice,
p_memory_properties: *mut vk::PhysicalDeviceMemoryProperties,
) {
MOCK_CALLED.store(true, Ordering::SeqCst);
unsafe {
let mut props: vk::PhysicalDeviceMemoryProperties = std::mem::zeroed();
props.memory_type_count = 3;
props.memory_heap_count = 2;
*p_memory_properties = props;
}
}
#[test]
fn pattern_create_returns_handle() {
let _lock = TEST_MUTEX.lock().expect("TEST_MUTEX poisoned");
reset_mocks();
let device = mock_device();
let fence_info = vk::FenceCreateInfo {
s_type: vk::StructureType::FENCE_CREATE_INFO,
p_next: std::ptr::null(),
flags: vk::FenceCreateFlagBits::empty(),
};
let fence =
unsafe { device.create_fence(&fence_info, None) }.expect("create_fence should succeed");
assert!(MOCK_CALLED.load(Ordering::SeqCst), "mock was not called");
assert_eq!(fence.as_raw(), 0xFE01, "wrong fence handle returned");
}
#[test]
fn pattern_destroy_forwards_args() {
let _lock = TEST_MUTEX.lock().expect("TEST_MUTEX poisoned");
reset_mocks();
let device = mock_device();
let fence = vk::Fence::from_raw(0xFE01);
unsafe { device.destroy_fence(fence, None) };
assert!(MOCK_CALLED.load(Ordering::SeqCst), "mock was not called");
}
#[test]
fn pattern_enumerate_returns_vec() {
let _lock = TEST_MUTEX.lock().expect("TEST_MUTEX poisoned");
reset_mocks();
let instance = mock_instance();
let devices =
unsafe { instance.enumerate_physical_devices() }.expect("enumerate should succeed");
assert!(MOCK_CALLED.load(Ordering::SeqCst), "mock was not called");
assert_eq!(devices.len(), 2, "expected 2 physical devices");
}
#[test]
fn pattern_fill_returns_populated_vec() {
let _lock = TEST_MUTEX.lock().expect("TEST_MUTEX poisoned");
reset_mocks();
let instance = mock_instance();
let physical_device = vk::PhysicalDevice::from_raw(0xABCD);
let families = unsafe { instance.get_physical_device_queue_family_properties(physical_device) };
assert!(MOCK_CALLED.load(Ordering::SeqCst), "mock was not called");
assert_eq!(families.len(), 1, "expected 1 queue family");
assert_eq!(families[0].queue_count, 4);
assert!(
families[0]
.queue_flags
.contains(vk::QueueFlagBits::GRAPHICS)
);
}
#[test]
fn pattern_query_returns_struct() {
let _lock = TEST_MUTEX.lock().expect("TEST_MUTEX poisoned");
reset_mocks();
let instance = mock_instance();
let physical_device = vk::PhysicalDevice::from_raw(0xABCD);
let mem_props = unsafe { instance.get_physical_device_memory_properties(physical_device) };
assert!(MOCK_CALLED.load(Ordering::SeqCst), "mock was not called");
assert_eq!(mem_props.memory_type_count, 3);
assert_eq!(mem_props.memory_heap_count, 2);
}
#[test]
fn pattern_result_only_returns_ok() {
let _lock = TEST_MUTEX.lock().expect("TEST_MUTEX poisoned");
reset_mocks();
let device = mock_device();
let result = unsafe { device.device_wait_idle() };
assert!(MOCK_CALLED.load(Ordering::SeqCst), "mock was not called");
assert!(result.is_ok(), "device_wait_idle should return Ok");
}
#[test]
fn pattern_void_forward_passes_all_args() {
let _lock = TEST_MUTEX.lock().expect("TEST_MUTEX poisoned");
reset_mocks();
let device = mock_device();
let cmd_buf = vk::CommandBuffer::from_raw(0xCB01);
unsafe { device.cmd_draw(cmd_buf, 36, 1, 0, 0) };
assert!(MOCK_CALLED.load(Ordering::SeqCst), "mock was not called");
assert_eq!(MOCK_VERTEX_COUNT.load(Ordering::SeqCst), 36);
assert_eq!(MOCK_INSTANCE_COUNT.load(Ordering::SeqCst), 1);
}