use super::*;
use crate::entities::EntityHandle;
use crate::test_support::entities::{
MockEntity, input, set_accepts, set_datamap, state_maps, take_inputs,
};
use crate::test_support::server_tools::MockTools;
use std::ffi::CString;
use std::ptr::NonNull;
const CHILD: u32 = 7 | 3 << 16;
const GRANDPARENT: u32 = 9 | 2 << 16;
const PARENT: u32 = 8 | 1 << 16;
struct Mocks {
tools: MockTools,
child: MockEntity,
parent: MockEntity,
grandparent: MockEntity,
}
#[test]
fn attachments_are_followed_once_found() {
let mut mocks = mocks();
let tools = mocks.tools.tools();
let child = entity(&mut mocks.child);
let parent = entity(&mut mocks.parent);
mocks.child.state().parent_attachment = 2;
assert_eq!(child.parent_attachment(), Ok(NonZeroU8::new(2)));
tools
.set_parent_attachment(child, parent, c"head", false)
.unwrap();
tools
.set_parent_attachment(child, parent, c"head", true)
.unwrap();
let set_parent = (
c"SetParent".to_owned(),
parent.as_ptr(),
child.as_ptr(),
Some(c"!activator".to_owned()),
);
let attach = |input: &CStr| {
(
input.to_owned(),
parent.as_ptr(),
child.as_ptr(),
Some(c"head".to_owned()),
)
};
assert_eq!(
received(child),
[
set_parent.clone(),
attach(c"SetParentAttachment"),
set_parent,
attach(c"SetParentAttachmentMaintainOffset"),
]
);
mocks.child.state().parent_attachment = 0;
assert_eq!(child.parent_attachment(), Ok(None));
assert_eq!(
tools.set_parent_attachment(child, parent, c"missing", false),
Err(ParentError::UnknownAttachment)
);
assert_eq!(received(child).len(), 2);
assert_eq!(
tools.set_parent_attachment(child, child, c"head", false),
Err(ParentError::Cycle)
);
assert!(received(child).is_empty());
}
#[test]
fn cycles_are_refused() {
let mut mocks = mocks();
let tools = mocks.tools.tools();
let child = entity(&mut mocks.child);
let parent = entity(&mut mocks.parent);
assert_eq!(tools.set_parent(child, child), Err(ParentError::Cycle));
mocks.parent.state().move_parent = GRANDPARENT;
mocks.grandparent.state().move_parent = CHILD;
assert_eq!(tools.set_parent(child, parent), Err(ParentError::Cycle));
mocks.grandparent.state().move_parent = PARENT;
assert_eq!(tools.set_parent(child, parent), Err(ParentError::Cycle));
assert!(take_inputs().is_empty());
mocks.grandparent.state().move_parent = EntityHandle::INVALID.to_raw();
tools.set_parent(child, parent).unwrap();
mocks.grandparent.state().move_parent = CHILD + (1 << 16);
tools.set_parent(child, parent).unwrap();
assert_eq!(received(child).len(), 2);
}
fn entity(mock: &mut MockEntity) -> Entity<'static> {
unsafe { Entity::from_raw(NonNull::new(mock.as_ptr()).unwrap()) }
}
fn mocks() -> Mocks {
use sys::{_fieldtypes_FIELD_STRING as STRING, _fieldtypes_FIELD_VOID as VOID};
let mut world = MockEntity::new(0);
let mut mocks = Mocks {
tools: MockTools::new(world.as_ptr()),
child: MockEntity::new(CHILD),
parent: MockEntity::new(PARENT),
grandparent: MockEntity::new(GRANDPARENT),
};
set_datamap(state_maps(vec![(
c"CTestEntity",
vec![
input(c"ClearParent", VOID),
input(c"SetParent", STRING),
input(c"SetParentAttachment", STRING),
input(c"SetParentAttachmentMaintainOffset", STRING),
],
)]));
for (mock, handle) in [
(&mut mocks.child, CHILD),
(&mut mocks.parent, PARENT),
(&mut mocks.grandparent, GRANDPARENT),
] {
mocks
.tools
.list(mock.as_ptr(), EntityHandle::from_raw(handle));
mock.state().move_parent = EntityHandle::INVALID.to_raw();
}
set_accepts(true);
take_inputs();
mocks
}
#[test]
fn move_children_are_listed_from_the_last_parented() {
let mut mocks = mocks();
let tools = mocks.tools.tools();
let child = entity(&mut mocks.child);
let parent = entity(&mut mocks.parent);
let grandparent = entity(&mut mocks.grandparent);
for mock in [&mut mocks.child, &mut mocks.parent, &mut mocks.grandparent] {
mock.state().move_child = EntityHandle::INVALID.to_raw();
mock.state().move_peer = EntityHandle::INVALID.to_raw();
}
assert_eq!(grandparent.move_child(), Ok(None));
assert_eq!(tools.move_children(grandparent), Ok(Vec::new()));
mocks.grandparent.state().move_child = CHILD;
mocks.child.state().move_peer = PARENT;
assert_eq!(
grandparent.move_child(),
Ok(Some(EntityHandle::from_raw(CHILD)))
);
assert_eq!(child.move_peer(), Ok(Some(EntityHandle::from_raw(PARENT))));
assert_eq!(parent.move_peer(), Ok(None));
assert_eq!(tools.move_children(grandparent), Ok(vec![child, parent]));
mocks.child.state().move_peer = PARENT + (1 << 16);
assert_eq!(tools.move_children(grandparent), Ok(vec![child]));
mocks.child.state().move_peer = PARENT;
mocks.parent.state().move_peer = CHILD;
let children = tools.move_children(grandparent).unwrap();
assert_eq!(children.len(), MAX_MOVE_CHILDREN);
assert_eq!(children[..3], [child, parent, child]);
}
#[test]
fn parents_are_set_and_cleared_through_inputs() {
let mut mocks = mocks();
let tools = mocks.tools.tools();
let child = entity(&mut mocks.child);
let parent = entity(&mut mocks.parent);
tools.set_parent(child, parent).unwrap();
tools.clear_parent(child).unwrap();
assert_eq!(
received(child),
[
(
c"SetParent".to_owned(),
parent.as_ptr(),
child.as_ptr(),
Some(c"!activator".to_owned())
),
(
c"ClearParent".to_owned(),
child.as_ptr(),
child.as_ptr(),
None
),
]
);
set_accepts(false);
assert_eq!(
tools.set_parent(child, parent),
Err(ParentError::Input(InputError::Rejected))
);
assert_eq!(tools.clear_parent(child), Err(InputError::Rejected));
}
fn received(
target: Entity<'_>,
) -> Vec<(
CString,
*mut sys::CBaseEntity,
*mut sys::CBaseEntity,
Option<CString>,
)> {
take_inputs()
.into_iter()
.map(|input| {
assert_eq!(input.target, target.as_ptr());
let string = (!input.string.is_null())
.then(|| unsafe { CStr::from_ptr(input.string) }.to_owned());
(input.name, input.activator, input.caller, string)
})
.collect()
}