use crate::{
BehaviorTreeBundle, BehaviorTreePlugin,
node::{DelegateNode, prelude::*},
task::{TaskBridge, TaskStatus},
};
use bevy::diagnostic::{FrameCount, FrameCountPlugin};
use bevy::prelude::*;
pub mod prelude {
pub use super::{TestLog, TestLogEntry, TesterPlugin, TesterTask};
pub use crate::prelude::*;
pub use bevy::prelude::*;
}
pub struct TesterPlugin;
impl Plugin for TesterPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(FrameCountPlugin)
.add_systems(Update, update::<0>)
.add_systems(Update, update::<1>)
.add_systems(Update, update::<2>)
.add_systems(Update, update::<3>)
.add_systems(Update, update::<4>)
.add_systems(Update, update::<5>)
.add_systems(Update, update::<6>)
.add_systems(Update, update::<7>)
.init_resource::<TestLog>();
}
}
pub struct TesterTask<const ID: i32> {
task: TaskBridge,
}
impl<const ID: i32> DelegateNode for TesterTask<ID> {
fn delegate_node(&self) -> &dyn crate::node::Node {
&self.task
}
}
impl<const ID: i32> TesterTask<ID> {
pub fn new(count: u32, result: NodeResult) -> Self {
let checker = move |In(entity): In<Entity>, param: Query<&TesterComponent<ID>>| {
let comp = param.get(entity).expect("TesterComponent not found!");
if comp.updated_count < count {
TaskStatus::Running
} else {
TaskStatus::Complete(result)
}
};
let task = TaskBridge::new(checker)
.insert_while_running(TesterComponent::<ID> { updated_count: 0 });
Self { task }
}
}
#[derive(Debug, Component, Clone, Copy)]
pub struct TesterComponent<const ID: i32> {
pub updated_count: u32,
}
#[derive(Debug, Resource, Default, PartialEq, Eq)]
pub struct TestLog {
pub log: Vec<TestLogEntry>,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub struct TestLogEntry {
pub task_id: u32,
pub updated_count: u32,
pub frame: u32,
}
fn update<const ID: i32>(
mut log: ResMut<TestLog>,
mut comps: Query<&mut TesterComponent<ID>>,
frame: Res<FrameCount>,
) {
for mut comp in comps.iter_mut() {
log.log.push(TestLogEntry {
task_id: ID as u32,
updated_count: comp.updated_count,
frame: frame.0,
});
comp.updated_count += 1;
}
}
#[test]
fn test_enter_tester_task() {
let mut app = App::new();
app.add_plugins((BehaviorTreePlugin::default(), TesterPlugin));
let task = TesterTask::<0>::new(1, NodeResult::Success);
let entity = app
.world_mut()
.spawn(BehaviorTreeBundle::from_root(task))
.id();
app.update();
assert!(
app.world().get::<TesterComponent<0>>(entity).is_some(),
"TesterComponent should added on enter."
);
app.update();
}
#[test]
fn test_exit_tester_task() {
let mut app = App::new();
app.add_plugins((BehaviorTreePlugin::default(), TesterPlugin));
let task = TesterTask::<0>::new(1, NodeResult::Success);
let entity = app
.world_mut()
.spawn(BehaviorTreeBundle::from_root(task))
.id();
app.update();
app.update();
assert!(
app.world().get::<TesterComponent<0>>(entity).is_none(),
"TesterComponent should removed on exit."
);
}
#[test]
fn test_log_test_task() {
let mut app = App::new();
app.add_plugins((BehaviorTreePlugin::default(), TesterPlugin));
let task = TesterTask::<0>::new(1, NodeResult::Success);
let _entity = app
.world_mut()
.spawn(BehaviorTreeBundle::from_root(task))
.id();
app.update();
app.update();
let expected = TestLog {
log: vec![TestLogEntry {
task_id: 0,
updated_count: 0,
frame: 1,
}],
};
let found = app.world().get_resource::<TestLog>().unwrap();
assert!(
found == &expected,
"TesterComponent should removed on exit. found: {:?}",
found
);
}