Add tests for HSM
This commit is contained in:
parent
fb98d2f34e
commit
0e10c2022f
|
@ -179,7 +179,7 @@ bool LimboHSM::dispatch(const String &p_event, const Variant &p_cargo) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!event_consumed && p_event == EVENT_FINISHED && !(get_parent()->is_class("LimboState"))) {
|
||||
if (!event_consumed && p_event == EVENT_FINISHED && !(get_parent() && get_parent()->is_class("LimboState"))) {
|
||||
_exit();
|
||||
}
|
||||
|
||||
|
|
|
@ -24,10 +24,12 @@ public:
|
|||
int num_callbacks = 0;
|
||||
|
||||
void callback() { num_callbacks += 1; }
|
||||
void callback_delta(double delta) { num_callbacks += 1; }
|
||||
|
||||
protected:
|
||||
static void _bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("callback"), &CallbackCounter::callback);
|
||||
ClassDB::bind_method(D_METHOD("callback_delta", "delta"), &CallbackCounter::callback_delta);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
/**
|
||||
* test_hsm.h
|
||||
* =============================================================================
|
||||
* Copyright 2021-2023 Serhii Snitsaruk
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
* =============================================================================
|
||||
*/
|
||||
|
||||
#ifndef TEST_HSM_H
|
||||
#define TEST_HSM_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/hsm/limbo_hsm.h"
|
||||
#include "modules/limboai/hsm/limbo_state.h"
|
||||
|
||||
#include "core/object/object.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "core/os/memory.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
namespace TestHSM {
|
||||
|
||||
class TestGuard : public RefCounted {
|
||||
GDCLASS(TestGuard, RefCounted);
|
||||
|
||||
public:
|
||||
bool permitted_to_enter = false;
|
||||
bool can_enter() { return permitted_to_enter; }
|
||||
};
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] HSM") {
|
||||
Node *agent = memnew(Node);
|
||||
LimboHSM *hsm = memnew(LimboHSM);
|
||||
|
||||
Ref<CallbackCounter> alpha_entries = memnew(CallbackCounter);
|
||||
Ref<CallbackCounter> alpha_exits = memnew(CallbackCounter);
|
||||
Ref<CallbackCounter> alpha_updates = memnew(CallbackCounter);
|
||||
Ref<CallbackCounter> beta_entries = memnew(CallbackCounter);
|
||||
Ref<CallbackCounter> beta_exits = memnew(CallbackCounter);
|
||||
Ref<CallbackCounter> beta_updates = memnew(CallbackCounter);
|
||||
|
||||
LimboState *state_alpha = memnew(LimboState);
|
||||
state_alpha->call_on_enter(callable_mp(alpha_entries.ptr(), &CallbackCounter::callback));
|
||||
state_alpha->call_on_update(callable_mp(alpha_updates.ptr(), &CallbackCounter::callback_delta));
|
||||
state_alpha->call_on_exit(callable_mp(alpha_exits.ptr(), &CallbackCounter::callback));
|
||||
|
||||
LimboState *state_beta = memnew(LimboState);
|
||||
state_beta->call_on_enter(callable_mp(beta_entries.ptr(), &CallbackCounter::callback));
|
||||
state_beta->call_on_update(callable_mp(beta_updates.ptr(), &CallbackCounter::callback_delta));
|
||||
state_beta->call_on_exit(callable_mp(beta_exits.ptr(), &CallbackCounter::callback));
|
||||
|
||||
hsm->add_child(state_alpha);
|
||||
hsm->add_child(state_beta);
|
||||
|
||||
hsm->add_transition(state_alpha, state_beta, "event_one");
|
||||
hsm->add_transition(state_beta, state_alpha, "event_two");
|
||||
|
||||
hsm->set_initial_state(state_alpha);
|
||||
hsm->initialize(agent);
|
||||
hsm->set_active(true);
|
||||
|
||||
SUBCASE("Test get_root()") {
|
||||
CHECK(state_alpha->get_root() == hsm);
|
||||
CHECK(state_beta->get_root() == hsm);
|
||||
CHECK(hsm->get_root() == hsm);
|
||||
}
|
||||
SUBCASE("Test with basic workflow and transitions") {
|
||||
REQUIRE(hsm->is_active());
|
||||
REQUIRE(hsm->get_active_state() == state_alpha);
|
||||
CHECK(alpha_entries->num_callbacks == 1); // * entered
|
||||
CHECK(alpha_updates->num_callbacks == 0);
|
||||
CHECK(alpha_exits->num_callbacks == 0);
|
||||
CHECK(beta_entries->num_callbacks == 0);
|
||||
CHECK(beta_updates->num_callbacks == 0);
|
||||
CHECK(beta_exits->num_callbacks == 0);
|
||||
|
||||
hsm->update(0.01666);
|
||||
CHECK(alpha_entries->num_callbacks == 1);
|
||||
CHECK(alpha_updates->num_callbacks == 1); // * updated
|
||||
CHECK(alpha_exits->num_callbacks == 0);
|
||||
CHECK(beta_entries->num_callbacks == 0);
|
||||
CHECK(beta_updates->num_callbacks == 0);
|
||||
CHECK(beta_exits->num_callbacks == 0);
|
||||
|
||||
hsm->update(0.01666);
|
||||
CHECK(alpha_entries->num_callbacks == 1);
|
||||
CHECK(alpha_updates->num_callbacks == 2); // * updated x2
|
||||
CHECK(alpha_exits->num_callbacks == 0);
|
||||
CHECK(beta_entries->num_callbacks == 0);
|
||||
CHECK(beta_updates->num_callbacks == 0);
|
||||
CHECK(beta_exits->num_callbacks == 0);
|
||||
|
||||
hsm->dispatch("event_one");
|
||||
REQUIRE(hsm->get_active_state() == state_beta);
|
||||
CHECK(alpha_entries->num_callbacks == 1);
|
||||
CHECK(alpha_updates->num_callbacks == 2);
|
||||
CHECK(alpha_exits->num_callbacks == 1); // * (1) exited
|
||||
CHECK(beta_entries->num_callbacks == 1); // * (2) entered
|
||||
CHECK(beta_updates->num_callbacks == 0);
|
||||
CHECK(beta_exits->num_callbacks == 0);
|
||||
|
||||
hsm->update(0.01666);
|
||||
CHECK(alpha_entries->num_callbacks == 1);
|
||||
CHECK(alpha_updates->num_callbacks == 2);
|
||||
CHECK(alpha_exits->num_callbacks == 1);
|
||||
CHECK(beta_entries->num_callbacks == 1);
|
||||
CHECK(beta_updates->num_callbacks == 1); // * updated
|
||||
CHECK(beta_exits->num_callbacks == 0);
|
||||
|
||||
hsm->update(0.01666);
|
||||
CHECK(alpha_entries->num_callbacks == 1);
|
||||
CHECK(alpha_updates->num_callbacks == 2);
|
||||
CHECK(alpha_exits->num_callbacks == 1);
|
||||
CHECK(beta_entries->num_callbacks == 1);
|
||||
CHECK(beta_updates->num_callbacks == 2); // * updated x2
|
||||
CHECK(beta_exits->num_callbacks == 0);
|
||||
|
||||
hsm->dispatch("event_two");
|
||||
REQUIRE(hsm->get_active_state() == state_alpha);
|
||||
CHECK(alpha_entries->num_callbacks == 2); // * (2) entered
|
||||
CHECK(alpha_updates->num_callbacks == 2);
|
||||
CHECK(alpha_exits->num_callbacks == 1);
|
||||
CHECK(beta_entries->num_callbacks == 1);
|
||||
CHECK(beta_updates->num_callbacks == 2);
|
||||
CHECK(beta_exits->num_callbacks == 1); // * (1) exited
|
||||
|
||||
hsm->update(0.01666);
|
||||
CHECK(alpha_entries->num_callbacks == 2);
|
||||
CHECK(alpha_updates->num_callbacks == 3); // * updated
|
||||
CHECK(alpha_exits->num_callbacks == 1);
|
||||
CHECK(beta_entries->num_callbacks == 1);
|
||||
CHECK(beta_updates->num_callbacks == 2);
|
||||
CHECK(beta_exits->num_callbacks == 1);
|
||||
|
||||
hsm->dispatch(LimboState::EVENT_FINISHED);
|
||||
CHECK(alpha_entries->num_callbacks == 2);
|
||||
CHECK(alpha_updates->num_callbacks == 3);
|
||||
CHECK(alpha_exits->num_callbacks == 2); // * exited
|
||||
CHECK(beta_entries->num_callbacks == 1);
|
||||
CHECK(beta_updates->num_callbacks == 2);
|
||||
CHECK(beta_exits->num_callbacks == 1);
|
||||
CHECK_FALSE(hsm->is_active()); // * not active
|
||||
CHECK(hsm->get_active_state() == nullptr);
|
||||
}
|
||||
SUBCASE("Test transition with guard") {
|
||||
Ref<TestGuard> guard = memnew(TestGuard);
|
||||
state_beta->set_guard(callable_mp(guard.ptr(), &TestGuard::can_enter));
|
||||
|
||||
SUBCASE("When entry is permitted") {
|
||||
guard->permitted_to_enter = true;
|
||||
hsm->dispatch("event_one");
|
||||
CHECK(hsm->get_active_state() == state_beta);
|
||||
CHECK(alpha_exits->num_callbacks == 1);
|
||||
CHECK(beta_entries->num_callbacks == 1);
|
||||
}
|
||||
SUBCASE("When entry is not permitted") {
|
||||
guard->permitted_to_enter = false;
|
||||
hsm->dispatch("event_one");
|
||||
CHECK(hsm->get_active_state() == state_alpha);
|
||||
CHECK(alpha_exits->num_callbacks == 0);
|
||||
CHECK(beta_entries->num_callbacks == 0);
|
||||
}
|
||||
}
|
||||
SUBCASE("When there is no transition for given event") {
|
||||
hsm->dispatch("not_found");
|
||||
CHECK(alpha_exits->num_callbacks == 0);
|
||||
CHECK(beta_entries->num_callbacks == 0);
|
||||
CHECK(hsm->is_active());
|
||||
CHECK(hsm->get_active_state() == state_alpha);
|
||||
}
|
||||
|
||||
memdelete(agent);
|
||||
memdelete(hsm);
|
||||
}
|
||||
|
||||
} //namespace TestHSM
|
||||
|
||||
#endif // TEST_HSM_H
|
Loading…
Reference in New Issue