use sdk_raw::test_support::{mock_vtable, unexpected_call};
use source_sdk_2013::Module;
use source_sdk_2013::interfaces::ModelInfo;
use source_sdk_2013::test_support::leak;
use source_sdk_2013::test_support::server::{export, mock_server};
use std::cell::Cell;
use std::ffi::c_int;
use std::mem::zeroed;
use std::ptr::{null, null_mut};
const MODEL: *const sys::model_t = 0x10 as *const sys::model_t;
thread_local! {
static STUDIO: Cell<*mut sys::studiohdr_t> = const { Cell::new(null_mut()) };
static VCOLLIDE: Cell<*mut sys::vcollide_t> = const { Cell::new(null_mut()) };
}
unsafe extern "C" fn get_model(_: *mut sys::IVModelInfo, index: c_int) -> *const sys::model_t {
if index == -2 { MODEL } else { null() }
}
unsafe extern "C" fn get_vcollide(
_: *mut sys::IVModelInfo,
model: *const sys::model_t,
) -> *mut sys::vcollide_t {
assert_eq!(model, MODEL);
VCOLLIDE.get()
}
unsafe extern "C" fn get_studiomodel(
_: *mut sys::IVModelInfo,
model: *const sys::model_t,
) -> *mut sys::studiohdr_t {
assert_eq!(model, MODEL);
STUDIO.get()
}
#[test]
fn missing_models_are_recognized_by_their_stand_in() {
let vtable = unsafe {
mock_vtable::<sys::IVModelInfo__bindgen_vtable>(unexpected_call as *const (), |vtable| {
(&raw mut (*vtable).IVModelInfo_GetModel).write(get_model);
(&raw mut (*vtable).IVModelInfo_GetStudiomodel).write(get_studiomodel);
(&raw mut (*vtable).IVModelInfo_GetVCollide).write(get_vcollide);
})
};
export(
Module::Engine,
ModelInfo::VERSION,
leak(sys::IVModelInfo {
vtable_: Box::leak(vtable),
}),
);
let scope = ();
let info = mock_server(&scope).model_info().unwrap();
STUDIO.set(studio(b"error.mdl"));
assert_eq!(info.studio_name(-2).as_deref(), Some(c"error.mdl"));
assert!(info.is_error_model(-2));
STUDIO.set(studio(br"player\items\scout\bonk_helmet.mdl"));
assert!(!info.is_error_model(-2));
STUDIO.set(studio(&[b'a'; 64]));
assert_eq!(
info.studio_name(-2).map(|name| name.to_bytes().len()),
Some(64)
);
assert_eq!(info.studio_name(5), None);
assert!(!info.is_error_model(5));
STUDIO.set(null_mut());
assert_eq!(info.studio_name(-2), None);
}
#[test]
fn solids_are_counted_in_the_collision_model_bit_field() {
let vtable = unsafe {
mock_vtable::<sys::IVModelInfo__bindgen_vtable>(unexpected_call as *const (), |vtable| {
(&raw mut (*vtable).IVModelInfo_GetModel).write(get_model);
(&raw mut (*vtable).IVModelInfo_GetVCollide).write(get_vcollide);
})
};
export(
Module::Engine,
ModelInfo::VERSION,
leak(sys::IVModelInfo {
vtable_: Box::leak(vtable),
}),
);
let scope = ();
let info = mock_server(&scope).model_info().unwrap();
VCOLLIDE.set(leak(sys::vcollide_t {
_bitfield_1: sys::vcollide_t::new_bitfield_1(17, 1),
descSize: 0,
solids: null_mut(),
pKeyValues: null_mut(),
}));
assert_eq!(info.vcollide_solid_count(-2), Some(17));
assert_eq!(info.vcollide_solid_count(5), None);
VCOLLIDE.set(null_mut());
assert_eq!(info.vcollide_solid_count(-2), None);
}
fn studio(name: &[u8]) -> *mut sys::studiohdr_t {
let mut header: sys::studiohdr_t = unsafe { zeroed() };
for (slot, &byte) in header.name.iter_mut().zip(name) {
*slot = byte as _;
}
leak(header)
}