use bevy::prelude::*;
use bevy_gearbox::prelude::*;
use bevy_gearbox::GearboxPlugin;
#[derive(Component, Clone, Debug, PartialEq)]
struct Speed(f32);
#[derive(Component, Clone, Debug, PartialEq)]
struct Jumping;
#[derive(Component, Clone, Debug, PartialEq)]
struct CanMove;
#[test]
fn state_component_inserted_on_enter() {
let mut app = App::new();
app.add_plugins((MinimalPlugins, GearboxPlugin::default()));
app.register_state_component::<Speed>();
let world = app.world_mut();
let machine = world.spawn_empty().id();
let a = world
.spawn((SubstateOf(machine), StateComponent(Speed(5.0))))
.id();
world
.entity_mut(machine)
.insert((StateMachine::new(), InitialState(a)));
app.update();
assert_eq!(
app.world().get::<Speed>(machine).map(|s| s.0),
Some(5.0),
"Speed should be on machine root after entering A"
);
}
#[test]
fn state_component_removed_on_exit() {
let mut app = App::new();
app.add_plugins((MinimalPlugins, GearboxPlugin::default()));
app.register_state_component::<Speed>();
let world = app.world_mut();
let machine = world.spawn_empty().id();
let a = world
.spawn((SubstateOf(machine), StateComponent(Speed(5.0))))
.id();
let b = world.spawn(SubstateOf(machine)).id();
world
.entity_mut(machine)
.insert((StateMachine::new(), InitialState(a)));
app.update();
assert!(app.world().get::<Speed>(machine).is_some());
app.world_mut().write_message(TransitionMessage::new(machine, a, b, None));
app.update();
assert!(
app.world().get::<Speed>(machine).is_none(),
"Speed should be removed when A is exited"
);
}
#[test]
fn state_inactive_component_removed_on_enter_restored_on_exit() {
let mut app = App::new();
app.add_plugins((MinimalPlugins, GearboxPlugin::default()));
app.register_state_component::<CanMove>();
let world = app.world_mut();
let machine = world.spawn_empty().id();
let a = world
.spawn((SubstateOf(machine), StateInactiveComponent(CanMove)))
.id();
let b = world.spawn(SubstateOf(machine)).id();
world
.entity_mut(machine)
.insert((StateMachine::new(), InitialState(a), CanMove));
app.update();
assert!(
app.world().get::<CanMove>(machine).is_none(),
"CanMove should be removed while A (StateInactiveComponent) is active"
);
app.world_mut().write_message(TransitionMessage::new(machine, a, b, None));
app.update();
assert!(
app.world().get::<CanMove>(machine).is_some(),
"CanMove should be restored after exiting A"
);
}
#[test]
fn different_state_components_swap_on_transition() {
let mut app = App::new();
app.add_plugins((MinimalPlugins, GearboxPlugin::default()));
app.register_state_component::<Speed>();
app.register_state_component::<Jumping>();
let world = app.world_mut();
let machine = world.spawn_empty().id();
let running = world
.spawn((SubstateOf(machine), StateComponent(Speed(3.0))))
.id();
let jumping = world
.spawn((SubstateOf(machine), StateComponent(Jumping)))
.id();
world
.entity_mut(machine)
.insert((StateMachine::new(), InitialState(running)));
app.update();
assert!(app.world().get::<Speed>(machine).is_some());
assert!(app.world().get::<Jumping>(machine).is_none());
app.world_mut().write_message(TransitionMessage::new(machine, running, jumping, None));
app.update();
assert!(
app.world().get::<Speed>(machine).is_none(),
"Speed should be removed after leaving running state"
);
assert!(
app.world().get::<Jumping>(machine).is_some(),
"Jumping should be present after entering jumping state"
);
}
#[test]
fn root_sourced_transition_removes_state_component_from_intermediate_parent() {
let mut app = App::new();
app.add_plugins((MinimalPlugins, GearboxPlugin::default()));
app.register_state_component::<Speed>();
let world = app.world_mut();
let machine = world.spawn_empty().id();
let a = world
.spawn((SubstateOf(machine), StateComponent(Speed(5.0))))
.id();
let a_child = world.spawn(SubstateOf(a)).id();
let b = world.spawn(SubstateOf(machine)).id();
world.entity_mut(a).insert(InitialState(a_child));
world
.entity_mut(machine)
.insert((StateMachine::new(), InitialState(a)));
app.update();
assert_eq!(
app.world().get::<Speed>(machine).map(|s| s.0),
Some(5.0),
"Speed should be on machine root after entering A"
);
app.world_mut().write_message(TransitionMessage::new(machine, machine, b, None));
app.update();
assert!(
app.world().get::<Speed>(machine).is_none(),
"Speed should be removed when A is exited via a root-sourced transition"
);
}
#[test]
fn state_component_on_nested_state_applies_to_root() {
let mut app = App::new();
app.add_plugins((MinimalPlugins, GearboxPlugin::default()));
app.register_state_component::<Speed>();
let world = app.world_mut();
let machine = world.spawn_empty().id();
let parent = world.spawn(SubstateOf(machine)).id();
let child = world
.spawn((SubstateOf(parent), StateComponent(Speed(1.0))))
.id();
world.entity_mut(parent).insert(InitialState(child));
world
.entity_mut(machine)
.insert((StateMachine::new(), InitialState(parent)));
app.update();
assert_eq!(
app.world().get::<Speed>(machine).map(|s| s.0),
Some(1.0),
"Speed should be on machine root even though the state is nested"
);
assert!(
app.world().get::<Speed>(parent).is_none(),
"Speed should NOT be on intermediate parent"
);
}