Merge branch 'unit-testing'
This commit is contained in:
commit
476c4fd497
|
@ -30,12 +30,12 @@ Variant BBNode::get_value(Object *p_agent, const Ref<Blackboard> &p_blackboard,
|
|||
ERR_FAIL_COND_V_MSG(agent == nullptr, Variant(), "BBNode: p_agent must be a Node.");
|
||||
return agent->get_node(val);
|
||||
} else {
|
||||
Node *node = Object::cast_to<Node>(val);
|
||||
if (unlikely(node == nullptr && val.get_type() != Variant::NIL)) {
|
||||
Object *obj = val;
|
||||
if (unlikely(obj == nullptr && val.get_type() != Variant::NIL)) {
|
||||
WARN_PRINT("BBNode: Unexpected variant type of a blackboard variable.");
|
||||
return p_default;
|
||||
} else {
|
||||
return node;
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ Variant BBParam::get_value(Object *p_agent, const Ref<Blackboard> &p_blackboard,
|
|||
if (value_source == SAVED_VALUE) {
|
||||
return saved_value;
|
||||
} else {
|
||||
ERR_FAIL_COND_V_MSG(!p_blackboard->has_var(variable), Variant(), vformat("BBParam: Blackboard variable doesn't exist: \"%s\".", p_default));
|
||||
ERR_FAIL_COND_V_MSG(!p_blackboard->has_var(variable), p_default, vformat("BBParam: Blackboard variable \"%s\" doesn't exist.", variable));
|
||||
return p_blackboard->get_var(variable, p_default);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -199,7 +199,7 @@ void BTPlayer::_bind_methods() {
|
|||
ADD_SIGNAL(MethodInfo("updated", PropertyInfo(Variant::INT, "p_status")));
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
ClassDB::bind_method(D_METHOD("_set_monitor_performance"), &BTPlayer::_set_monitor_performance);
|
||||
ClassDB::bind_method(D_METHOD("_set_monitor_performance", "p_value"), &BTPlayer::_set_monitor_performance);
|
||||
ClassDB::bind_method(D_METHOD("_get_monitor_performance"), &BTPlayer::_get_monitor_performance);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitor_performance"), "_set_monitor_performance", "_get_monitor_performance");
|
||||
ADD_PROPERTY_DEFAULT("monitor_performance", false);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "bt_set_var.h"
|
||||
|
||||
#include "modules/limboai/blackboard/bb_param/bb_param.h"
|
||||
#include "modules/limboai/util/limbo_utility.h"
|
||||
|
||||
#include "core/variant/callable.h"
|
||||
|
@ -24,9 +25,12 @@ String BTSetVar::_generate_name() const {
|
|||
}
|
||||
|
||||
int BTSetVar::_tick(double p_delta) {
|
||||
ERR_FAIL_COND_V_MSG(variable.is_empty(), FAILURE, "BBSetVar: `variable` is not set.");
|
||||
ERR_FAIL_COND_V_MSG(!value.is_valid(), FAILURE, "BBSetVar: `value` is not set.");
|
||||
get_blackboard()->set_var(variable, value->get_value(get_agent(), get_blackboard()));
|
||||
ERR_FAIL_COND_V_MSG(variable.is_empty(), FAILURE, "BTSetVar: `variable` is not set.");
|
||||
ERR_FAIL_COND_V_MSG(!value.is_valid(), FAILURE, "BTSetVar: `value` is not set.");
|
||||
Variant error_result = SNAME("Error: BTSetVar failed to get value!");
|
||||
Variant result = value->get_value(get_agent(), get_blackboard(), error_result);
|
||||
ERR_FAIL_COND_V_MSG(result == error_result, FAILURE, "BTSetVar: Failed to get parameter value. Returning FAILURE.");
|
||||
get_blackboard()->set_var(variable, result);
|
||||
return SUCCESS;
|
||||
};
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ int BTCallMethod::_tick(double p_delta) {
|
|||
ERR_FAIL_COND_V_MSG(method == StringName(), FAILURE, "BTCallMethod: Method Name is not set.");
|
||||
ERR_FAIL_COND_V_MSG(node_param.is_null(), FAILURE, "BTCallMethod: Node parameter is not set.");
|
||||
Object *obj = node_param->get_value(get_agent(), get_blackboard());
|
||||
ERR_FAIL_COND_V_MSG(obj == nullptr, FAILURE, "BTCallMethod: Failed to get node: " + node_param->to_string());
|
||||
ERR_FAIL_COND_V_MSG(obj == nullptr, FAILURE, "BTCallMethod: Failed to get object: " + node_param->to_string());
|
||||
|
||||
const Variant **argptrs = nullptr;
|
||||
|
||||
|
|
|
@ -48,9 +48,13 @@ int BTSetAgentProperty::_tick(double p_delta) {
|
|||
ERR_FAIL_COND_V_MSG(property == StringName(), FAILURE, "BTSetAgentProperty: `property` is not set.");
|
||||
ERR_FAIL_COND_V_MSG(!value.is_valid(), FAILURE, "BTSetAgentProperty: `value` is not set.");
|
||||
|
||||
StringName error_value = SNAME("ErrorGettingValue");
|
||||
Variant v = value->get_value(get_agent(), get_blackboard(), error_value);
|
||||
ERR_FAIL_COND_V_MSG(v == Variant(error_value), FAILURE, "BTSetAgentProperty: Couldn't get value of value-parameter.");
|
||||
|
||||
bool r_valid;
|
||||
get_agent()->set(property, value->get_value(get_agent(), get_blackboard()), &r_valid);
|
||||
ERR_FAIL_COND_V_MSG(!r_valid, FAILURE, vformat("BTSetAgentProperty: Agent doesn't have property named \"%s\"", property));
|
||||
get_agent()->set(property, v, &r_valid);
|
||||
ERR_FAIL_COND_V_MSG(!r_valid, FAILURE, vformat("BTSetAgentProperty: Couldn't set property \"%s\" with value \"%s\"", property, v));
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,9 +48,9 @@ void BTRandomWait::set_max_duration(double p_max_duration) {
|
|||
}
|
||||
|
||||
void BTRandomWait::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_min_duration"), &BTRandomWait::set_min_duration);
|
||||
ClassDB::bind_method(D_METHOD("set_min_duration", "p_value"), &BTRandomWait::set_min_duration);
|
||||
ClassDB::bind_method(D_METHOD("get_min_duration"), &BTRandomWait::get_min_duration);
|
||||
ClassDB::bind_method(D_METHOD("set_max_duration"), &BTRandomWait::set_max_duration);
|
||||
ClassDB::bind_method(D_METHOD("set_max_duration", "p_value"), &BTRandomWait::set_max_duration);
|
||||
ClassDB::bind_method(D_METHOD("get_max_duration"), &BTRandomWait::get_max_duration);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_duration"), "set_min_duration", "get_min_duration");
|
||||
|
|
|
@ -23,8 +23,8 @@ void BTWaitTicks::_enter() {
|
|||
}
|
||||
|
||||
int BTWaitTicks::_tick(double p_delta) {
|
||||
num_passed += 1;
|
||||
if (num_passed < num_ticks) {
|
||||
num_passed += 1;
|
||||
return RUNNING;
|
||||
} else {
|
||||
return SUCCESS;
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
Node-type parameter to be used with BT tasks. See [BBParam].
|
||||
</brief_description>
|
||||
<description>
|
||||
Node-type parameter to be used with BT tasks. See [BBParam].
|
||||
If blackboard is chosen as source, it allows any [Object]-extended type.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="BTCallMethod" inherits="BTAction" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
|
||||
<brief_description>
|
||||
BT action that calls a method on the specified [Node].
|
||||
BT action that calls a method on a specified [Node] or [Object].
|
||||
</brief_description>
|
||||
<description>
|
||||
BTCallMethod calls a method on the specified [Node] and returns [code]SUCCESS[/code].
|
||||
Returns [code]FAILURE[/code] if the action fails executing the node's method.
|
||||
BTCallMethod calls a method on a specified [Node] or [Object] and returns [code]SUCCESS[/code].
|
||||
Returns [code]FAILURE[/code] if the action fails executing the method.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
@ -14,10 +14,10 @@
|
|||
Arguments to pass to the method call.
|
||||
</member>
|
||||
<member name="method" type="StringName" setter="set_method" getter="get_method" default="&""">
|
||||
Node's method that will be called.
|
||||
Method that will be called.
|
||||
</member>
|
||||
<member name="node" type="BBNode" setter="set_node_param" getter="get_node_param">
|
||||
Node parameter that will be used to get the node instance.
|
||||
Parameter that will be used to get the node/object instance.
|
||||
</member>
|
||||
</members>
|
||||
</class>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "core/object/object.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
class LimboHSM : public LimboState {
|
||||
GDCLASS(LimboHSM, LimboState);
|
||||
|
@ -64,7 +65,7 @@ public:
|
|||
LimboState *get_initial_state() const { return initial_state; }
|
||||
|
||||
virtual void initialize(Node *p_agent, const Ref<Blackboard> &p_parent_scope = nullptr);
|
||||
virtual bool dispatch(const String &p_event, const Variant &p_cargo) override;
|
||||
virtual bool dispatch(const String &p_event, const Variant &p_cargo = Variant()) override;
|
||||
|
||||
void update(double p_delta) { _update(p_delta); }
|
||||
void add_transition(Node *p_from_state, Node *p_to_state, const String &p_event);
|
||||
|
|
|
@ -69,7 +69,7 @@ public:
|
|||
Node *get_agent() const { return agent; }
|
||||
void set_agent(Node *p_agent) { agent = p_agent; }
|
||||
|
||||
virtual bool dispatch(const String &p_event, const Variant &p_cargo);
|
||||
virtual bool dispatch(const String &p_event, const Variant &p_cargo = Variant());
|
||||
|
||||
LimboState *named(String p_name);
|
||||
LimboState *call_on_enter(const Callable &p_callable);
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* limbo_test.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 LIMBO_TEST_H
|
||||
#define LIMBO_TEST_H
|
||||
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_action.h"
|
||||
|
||||
class CallbackCounter : public RefCounted {
|
||||
GDCLASS(CallbackCounter, RefCounted);
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
class BTTestAction : public BTAction {
|
||||
GDCLASS(BTTestAction, BTAction);
|
||||
|
||||
public:
|
||||
int ret_status = BTTask::SUCCESS;
|
||||
int num_entries = 0;
|
||||
int num_ticks = 0;
|
||||
int num_exits = 0;
|
||||
|
||||
protected:
|
||||
virtual void _enter() override { num_entries += 1; }
|
||||
virtual void _exit() override { num_exits += 1; }
|
||||
|
||||
virtual int _tick(double p_delta) override {
|
||||
num_ticks += 1;
|
||||
return ret_status;
|
||||
}
|
||||
|
||||
public:
|
||||
bool is_status_either(int p_status1, int p_status2) { return (get_status() == p_status1 || get_status() == p_status2); }
|
||||
|
||||
BTTestAction(int p_return_status) { ret_status = p_return_status; }
|
||||
BTTestAction() {}
|
||||
};
|
||||
|
||||
#define CHECK_ENTRIES_TICKS_EXITS(m_task, m_entries, m_ticks, m_exits) \
|
||||
CHECK(m_task->num_entries == m_entries); \
|
||||
CHECK(m_task->num_ticks == m_ticks); \
|
||||
CHECK(m_task->num_exits == m_exits);
|
||||
|
||||
#define CHECK_ENTRIES_TICKS_EXITS_UP_TO(m_task, m_entries, m_ticks, m_exits) \
|
||||
CHECK(m_task->num_entries <= m_entries); \
|
||||
CHECK(m_task->num_ticks <= m_ticks); \
|
||||
CHECK(m_task->num_exits <= m_exits);
|
||||
|
||||
#define CHECK_STATUS_ENTRIES_TICKS_EXITS(m_task, m_status, m_entries, m_ticks, m_exits) \
|
||||
CHECK(m_task->get_status() == m_status); \
|
||||
CHECK(m_task->num_entries == m_entries); \
|
||||
CHECK(m_task->num_ticks == m_ticks); \
|
||||
CHECK(m_task->num_exits == m_exits);
|
||||
|
||||
#endif // LIMBO_TEST_H
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* test_always_fail.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_ALWAYS_FAIL_H
|
||||
#define TEST_ALWAYS_FAIL_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/decorators/bt_always_fail.h"
|
||||
|
||||
namespace TestAlwaysFail {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTAlwaysFail") {
|
||||
Ref<BTAlwaysFail> af = memnew(BTAlwaysFail);
|
||||
|
||||
SUBCASE("When empty") {
|
||||
CHECK(af->execute(0.01666) == BTTask::FAILURE);
|
||||
}
|
||||
|
||||
Ref<BTTestAction> task = memnew(BTTestAction);
|
||||
|
||||
af->add_child(task);
|
||||
|
||||
SUBCASE("When child returns FAILURE") {
|
||||
task->ret_status = BTTask::FAILURE;
|
||||
|
||||
CHECK(af->execute(0.01666) == BTTask::FAILURE);
|
||||
|
||||
CHECK(task->get_status() == BTTask::FAILURE);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 1);
|
||||
}
|
||||
|
||||
SUBCASE("When child returns SUCCESS") {
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
|
||||
CHECK(af->execute(0.01666) == BTTask::FAILURE);
|
||||
|
||||
CHECK(task->get_status() == BTTask::SUCCESS);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 1);
|
||||
}
|
||||
|
||||
SUBCASE("When child returns RUNNING") {
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
|
||||
CHECK(af->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task->get_status() == BTTask::RUNNING);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestAlwaysFail
|
||||
|
||||
#endif // TEST_ALWAYS_FAIL_H
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* test_always_succeed.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_ALWAYS_SUCCEED_H
|
||||
#define TEST_ALWAYS_SUCCEED_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/decorators/bt_always_succeed.h"
|
||||
|
||||
namespace TestAlwaysSucceed {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTAlwaysSucceed") {
|
||||
Ref<BTAlwaysSucceed> as = memnew(BTAlwaysSucceed);
|
||||
|
||||
SUBCASE("When empty") {
|
||||
CHECK(as->execute(0.01666) == BTTask::SUCCESS);
|
||||
}
|
||||
|
||||
Ref<BTTestAction> task = memnew(BTTestAction);
|
||||
|
||||
as->add_child(task);
|
||||
|
||||
SUBCASE("When child returns FAILURE") {
|
||||
task->ret_status = BTTask::FAILURE;
|
||||
|
||||
CHECK(as->execute(0.01666) == BTTask::SUCCESS);
|
||||
|
||||
CHECK(task->get_status() == BTTask::FAILURE);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 1);
|
||||
}
|
||||
|
||||
SUBCASE("When child returns SUCCESS") {
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
|
||||
CHECK(as->execute(0.01666) == BTTask::SUCCESS);
|
||||
|
||||
CHECK(task->get_status() == BTTask::SUCCESS);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 1);
|
||||
}
|
||||
|
||||
SUBCASE("When child returns RUNNING") {
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
|
||||
CHECK(as->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task->get_status() == BTTask::RUNNING);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestAlwaysSucceed
|
||||
|
||||
#endif // TEST_ALWAYS_SUCCEED_H
|
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* test_await_animation.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_AWAIT_ANIMATION_H
|
||||
#define TEST_AWAIT_ANIMATION_H
|
||||
|
||||
#include "core/os/memory.h"
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/blackboard/blackboard.h"
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/scene/bt_await_animation.h"
|
||||
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/resources/animation.h"
|
||||
#include "scene/resources/animation_library.h"
|
||||
|
||||
namespace TestAwaitAnimation {
|
||||
|
||||
TEST_CASE("[SceneTree][LimboAI] BTAwaitAnimation") {
|
||||
AnimationPlayer *player = memnew(AnimationPlayer);
|
||||
SceneTree::get_singleton()->get_root()->add_child(player);
|
||||
player->set_process_callback(AnimationPlayer::AnimationProcessCallback::ANIMATION_PROCESS_IDLE);
|
||||
|
||||
Ref<AnimationLibrary> anim_lib = memnew(AnimationLibrary);
|
||||
Ref<Animation> anim = memnew(Animation);
|
||||
anim->set_name("test");
|
||||
anim->set_length(0.1);
|
||||
anim->set_loop_mode(Animation::LOOP_NONE);
|
||||
REQUIRE(anim_lib->add_animation("test", anim) == OK);
|
||||
REQUIRE(player->add_animation_library("", anim_lib) == OK);
|
||||
REQUIRE(player->has_animation("test"));
|
||||
|
||||
Ref<BTAwaitAnimation> awa = memnew(BTAwaitAnimation);
|
||||
awa->set_animation_name("test");
|
||||
Ref<BBNode> player_param = memnew(BBNode);
|
||||
awa->set_animation_player(player_param);
|
||||
Node *dummy = memnew(Node);
|
||||
SceneTree::get_singleton()->get_root()->add_child(dummy);
|
||||
Ref<Blackboard> bb = memnew(Blackboard);
|
||||
|
||||
SUBCASE("When AnimationPlayer doesn't exist") {
|
||||
player_param->set_saved_value(NodePath("./NotFound"));
|
||||
ERR_PRINT_OFF;
|
||||
awa->initialize(dummy, bb);
|
||||
CHECK(awa->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("When AnimationPlayer exists") {
|
||||
player_param->set_saved_value(player->get_path());
|
||||
awa->initialize(dummy, bb);
|
||||
|
||||
SUBCASE("When AnimationPlayer is not playing") {
|
||||
REQUIRE_FALSE(player->is_playing());
|
||||
CHECK(awa->execute(0.01666) == BTTask::SUCCESS);
|
||||
}
|
||||
SUBCASE("When AnimationPlayer is playing") {
|
||||
player->play("test");
|
||||
REQUIRE(player->is_playing());
|
||||
REQUIRE(player->get_current_animation() == "test");
|
||||
CHECK(awa->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
SUBCASE("When exceeding max wait time") {
|
||||
awa->set_max_time(1.0);
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(awa->execute(1.0) == BTTask::SUCCESS);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("When animation finishes playing") {
|
||||
player->seek(888.0, true);
|
||||
player->notification(Node::NOTIFICATION_INTERNAL_PROCESS);
|
||||
CHECK_FALSE(player->is_playing());
|
||||
CHECK_FALSE(player->get_current_animation() == "test");
|
||||
CHECK(awa->execute(0.01666) == BTTask::SUCCESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memdelete(dummy);
|
||||
memdelete(player);
|
||||
}
|
||||
|
||||
} //namespace TestAwaitAnimation
|
||||
|
||||
#endif // TEST_AWAIT_ANIMATION_H
|
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* test_bb_param.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_BB_PARAM_H
|
||||
#define TEST_BB_PARAM_H
|
||||
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "core/string/node_path.h"
|
||||
#include "core/variant/variant.h"
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/blackboard/bb_param/bb_node.h"
|
||||
#include "modules/limboai/blackboard/bb_param/bb_param.h"
|
||||
#include "modules/limboai/blackboard/blackboard.h"
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestBBParam {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BBParam") {
|
||||
Ref<BBParam> param = memnew(BBParam);
|
||||
Node *dummy = memnew(Node);
|
||||
Ref<Blackboard> bb = memnew(Blackboard);
|
||||
|
||||
SUBCASE("Test with a value and common data types") {
|
||||
param->set_value_source(BBParam::SAVED_VALUE);
|
||||
|
||||
param->set_saved_value(123);
|
||||
CHECK(param->get_value(dummy, bb) == Variant(123));
|
||||
|
||||
param->set_saved_value("test");
|
||||
CHECK(param->get_value(dummy, bb) == Variant("test"));
|
||||
|
||||
param->set_saved_value(3.14);
|
||||
CHECK(param->get_value(dummy, bb) == Variant(3.14));
|
||||
}
|
||||
SUBCASE("Test with a BB variable") {
|
||||
param->set_value_source(BBParam::BLACKBOARD_VAR);
|
||||
param->set_variable("test_var");
|
||||
|
||||
SUBCASE("With integer") {
|
||||
bb->set_var("test_var", 123);
|
||||
CHECK(param->get_value(dummy, bb) == Variant(123));
|
||||
}
|
||||
SUBCASE("With String") {
|
||||
bb->set_var("test_var", "test");
|
||||
CHECK(param->get_value(dummy, bb) == Variant("test"));
|
||||
}
|
||||
SUBCASE("With float") {
|
||||
bb->set_var("test_var", 3.14);
|
||||
CHECK(param->get_value(dummy, bb) == Variant(3.14));
|
||||
}
|
||||
SUBCASE("When variable doesn't exist") {
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(param->get_value(dummy, bb, "default_value") == Variant("default_value"));
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
}
|
||||
|
||||
memdelete(dummy);
|
||||
}
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BBNode") {
|
||||
Ref<BBNode> param = memnew(BBNode);
|
||||
Node *dummy = memnew(Node);
|
||||
Ref<Blackboard> bb = memnew(Blackboard);
|
||||
|
||||
Node *other = memnew(Node);
|
||||
other->set_name("Other");
|
||||
dummy->add_child(other);
|
||||
|
||||
SUBCASE("With a valid path") {
|
||||
param->set_value_source(BBParam::SAVED_VALUE);
|
||||
param->set_saved_value(NodePath("./Other"));
|
||||
CHECK(param->get_value(dummy, bb).get_type() == Variant::Type::OBJECT);
|
||||
CHECK(param->get_value(dummy, bb) == Variant(other));
|
||||
}
|
||||
SUBCASE("With an invalid path") {
|
||||
param->set_value_source(BBParam::SAVED_VALUE);
|
||||
param->set_saved_value(NodePath("./SomeOther"));
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(param->get_value(dummy, bb, Variant()).is_null());
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("With an object on the blackboard") {
|
||||
param->set_value_source(BBParam::BLACKBOARD_VAR);
|
||||
param->set_variable("test_var");
|
||||
|
||||
SUBCASE("When variable exists") {
|
||||
bb->set_var("test_var", other);
|
||||
CHECK(param->get_value(dummy, bb).get_type() == Variant::Type::OBJECT);
|
||||
CHECK(param->get_value(dummy, bb) == Variant(other));
|
||||
}
|
||||
SUBCASE("When variable doesn't exist") {
|
||||
CHECK(param->get_value(dummy, bb, Variant()).is_null());
|
||||
}
|
||||
SUBCASE("When variable has wrong type") {
|
||||
bb->set_var("test_var", 123);
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(param->get_value(dummy, bb, Variant()).is_null());
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("When variable is an object") {
|
||||
// * Note: We allow also fetching objects on the blackboard.
|
||||
Ref<RefCounted> some_other = memnew(RefCounted);
|
||||
bb->set_var("test_var", some_other);
|
||||
CHECK(param->get_value(dummy, bb) == some_other);
|
||||
}
|
||||
}
|
||||
|
||||
memdelete(other);
|
||||
memdelete(dummy);
|
||||
}
|
||||
|
||||
} //namespace TestBBParam
|
||||
|
||||
#endif // TEST_BB_PARAM_H
|
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* test_call_method.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_CALL_METHOD_H
|
||||
#define TEST_CALL_METHOD_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/blackboard/bb_param/bb_node.h"
|
||||
#include "modules/limboai/blackboard/blackboard.h"
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/scene/bt_call_method.h"
|
||||
|
||||
#include "core/os/memory.h"
|
||||
|
||||
namespace TestCallMethod {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTCallMethod") {
|
||||
Ref<BTCallMethod> cm = memnew(BTCallMethod);
|
||||
|
||||
SUBCASE("When node parameter is null") {
|
||||
cm->set_node_param(nullptr);
|
||||
cm->set_method("test");
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(cm->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
SUBCASE("With object on the blackboard") {
|
||||
Node *dummy = memnew(Node);
|
||||
Ref<Blackboard> bb = memnew(Blackboard);
|
||||
|
||||
Ref<BBNode> node_param = memnew(BBNode);
|
||||
cm->set_node_param(node_param);
|
||||
Ref<CallbackCounter> callback_counter = memnew(CallbackCounter);
|
||||
bb->set_var("object", callback_counter);
|
||||
node_param->set_value_source(BBParam::BLACKBOARD_VAR);
|
||||
node_param->set_variable("object");
|
||||
cm->set_method("callback");
|
||||
|
||||
cm->initialize(dummy, bb);
|
||||
|
||||
SUBCASE("When method is empty") {
|
||||
cm->set_method("");
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(cm->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("When method doesn't exist") {
|
||||
cm->set_method("not_found");
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(cm->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("When method exists") {
|
||||
CHECK(cm->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK(callback_counter->num_callbacks == 1);
|
||||
}
|
||||
|
||||
memdelete(dummy);
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestCallMethod
|
||||
|
||||
#endif // TEST_CALL_METHOD_H
|
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* test_check_agent_property.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_CHECK_AGENT_PROPERTY_H
|
||||
#define TEST_CHECK_AGENT_PROPERTY_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/blackboard/bb_param/bb_variant.h"
|
||||
#include "modules/limboai/blackboard/blackboard.h"
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/scene/bt_check_agent_property.h"
|
||||
#include "modules/limboai/util/limbo_utility.h"
|
||||
|
||||
#include "core/os/memory.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
namespace TestCheckAgentProperty {
|
||||
|
||||
// Check with m_correct, m_incorrect and m_invalid values using m_check_type.
|
||||
#define TC_CHECK_AGENT_PROP(m_task, m_check_type, m_correct, m_incorrect, m_invalid) \
|
||||
m_task->set_check_type(m_check_type); \
|
||||
m_task->get_value()->set_saved_value(m_correct); \
|
||||
CHECK(m_task->execute(0.01666) == BTTask::SUCCESS); \
|
||||
m_task->get_value()->set_saved_value(m_incorrect); \
|
||||
CHECK(m_task->execute(0.01666) == BTTask::FAILURE); \
|
||||
m_task->get_value()->set_saved_value(m_invalid); \
|
||||
CHECK(m_task->execute(0.01666) == BTTask::FAILURE);
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTCheckAgentProperty") {
|
||||
Ref<BTCheckAgentProperty> cap = memnew(BTCheckAgentProperty);
|
||||
Node *agent = memnew(Node);
|
||||
Ref<Blackboard> bb = memnew(Blackboard);
|
||||
cap->initialize(agent, bb);
|
||||
StringName agent_name = "SimpleNode";
|
||||
agent->set_name(agent_name);
|
||||
|
||||
// * Defaults that should produce successful check:
|
||||
cap->set_property("name");
|
||||
cap->set_check_type(LimboUtility::CHECK_EQUAL);
|
||||
Ref<BBVariant> value = memnew(BBVariant);
|
||||
cap->set_value(value);
|
||||
value->set_saved_value(agent_name);
|
||||
REQUIRE(cap->execute(0.01666) == BTTask::SUCCESS);
|
||||
|
||||
SUBCASE("When property is not set") {
|
||||
cap->set_property("");
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(cap->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("When property is not found") {
|
||||
cap->set_property("not_found");
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(cap->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("When value is not set") {
|
||||
cap->set_value(nullptr);
|
||||
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(cap->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("With StringName") {
|
||||
StringName other_name = "OtherName";
|
||||
TC_CHECK_AGENT_PROP(cap, LimboUtility::CHECK_EQUAL, agent_name, other_name, 123);
|
||||
TC_CHECK_AGENT_PROP(cap, LimboUtility::CHECK_NOT_EQUAL, other_name, agent_name, 123);
|
||||
}
|
||||
SUBCASE("With integer") {
|
||||
cap->set_property("process_priority");
|
||||
TC_CHECK_AGENT_PROP(cap, LimboUtility::CHECK_EQUAL, 0, -1, "invalid");
|
||||
TC_CHECK_AGENT_PROP(cap, LimboUtility::CHECK_GREATER_THAN_OR_EQUAL, 0, 1, "invalid");
|
||||
TC_CHECK_AGENT_PROP(cap, LimboUtility::CHECK_GREATER_THAN, -1, 1, "invalid");
|
||||
TC_CHECK_AGENT_PROP(cap, LimboUtility::CHECK_LESS_THAN_OR_EQUAL, 0, -1, "invalid");
|
||||
TC_CHECK_AGENT_PROP(cap, LimboUtility::CHECK_LESS_THAN, 1, 0, "invalid");
|
||||
TC_CHECK_AGENT_PROP(cap, LimboUtility::CHECK_NOT_EQUAL, 1, 0, "invalid");
|
||||
}
|
||||
|
||||
memdelete(agent);
|
||||
}
|
||||
|
||||
} //namespace TestCheckAgentProperty
|
||||
|
||||
#endif // TEST_CHECK_AGENT_PROPERTY_H
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* test_check_trigger.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_CHECK_TRIGGER_H
|
||||
#define TEST_CHECK_TRIGGER_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/blackboard/bt_check_trigger.h"
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
|
||||
namespace TestCheckTrigger {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTCheckTrigger") {
|
||||
Ref<BTCheckTrigger> ct = memnew(BTCheckTrigger);
|
||||
Node *dummy = memnew(Node);
|
||||
Ref<Blackboard> bb = memnew(Blackboard);
|
||||
|
||||
ct->initialize(dummy, bb);
|
||||
|
||||
SUBCASE("Empty") {
|
||||
ERR_PRINT_OFF;
|
||||
ct->set_variable("");
|
||||
CHECK(ct->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
ct->set_variable("trigger");
|
||||
|
||||
SUBCASE("When variable is not set") {
|
||||
CHECK(ct->execute(0.01666) == BTTask::FAILURE);
|
||||
}
|
||||
SUBCASE("When variable set to false") {
|
||||
bb->set_var("trigger", false);
|
||||
CHECK(ct->execute(0.01666) == BTTask::FAILURE);
|
||||
CHECK(bb->get_var("trigger", false) == Variant(false));
|
||||
}
|
||||
SUBCASE("When variable set to true") {
|
||||
bb->set_var("trigger", true);
|
||||
CHECK(bb->get_var("trigger", false) == Variant(true));
|
||||
CHECK(ct->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK(bb->get_var("trigger", false) == Variant(false));
|
||||
}
|
||||
SUBCASE("When variable set to non-bool") {
|
||||
bb->set_var("trigger", "Some text");
|
||||
CHECK(ct->execute(0.01666) == BTTask::FAILURE);
|
||||
CHECK(bb->get_var("trigger", Variant()) == "Some text");
|
||||
}
|
||||
|
||||
memdelete(dummy);
|
||||
}
|
||||
|
||||
} //namespace TestCheckTrigger
|
||||
|
||||
#endif // TEST_CHECK_TRIGGER_H
|
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
* test_check_var.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_CHECK_VAR_H
|
||||
#define TEST_CHECK_VAR_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/blackboard/bb_param/bb_param.h"
|
||||
#include "modules/limboai/bt/tasks/blackboard/bt_check_var.h"
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/util/limbo_utility.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestCheckVar {
|
||||
|
||||
// Compare m_correct, m_incorrect and m_invalid to m_value based using m_check_type.
|
||||
#define TC_CHECK_VALUES(m_task, m_correct, m_incorrect, m_invalid, m_check_type, m_value) \
|
||||
m_task->get_value()->set_saved_value(m_value); \
|
||||
m_task->set_check_type(m_check_type); \
|
||||
m_task->get_blackboard()->set_var("var", m_correct); \
|
||||
CHECK(m_task->execute(0.01666) == BTTask::SUCCESS); \
|
||||
m_task->get_blackboard()->set_var("var", m_incorrect); \
|
||||
CHECK(m_task->execute(0.01666) == BTTask::FAILURE); \
|
||||
m_task->get_blackboard()->set_var("var", m_invalid); \
|
||||
CHECK(m_task->execute(0.01666) == BTTask::FAILURE);
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTCheckVar") {
|
||||
Ref<BTCheckVar> cv = memnew(BTCheckVar);
|
||||
Ref<Blackboard> bb = memnew(Blackboard);
|
||||
Node *dummy = memnew(Node);
|
||||
cv->initialize(dummy, bb);
|
||||
|
||||
SUBCASE("Check with empty variable and value") {
|
||||
cv->set_variable("");
|
||||
cv->set_value(nullptr);
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(cv->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("With variable and value set") {
|
||||
cv->set_variable("var");
|
||||
Ref<BBVariant> value = memnew(BBVariant);
|
||||
cv->set_value(value);
|
||||
|
||||
SUBCASE("When checking against another variable") {
|
||||
cv->set_check_type(LimboUtility::CHECK_EQUAL);
|
||||
value->set_value_source(BBParam::BLACKBOARD_VAR);
|
||||
bb->set_var("var", 123);
|
||||
SUBCASE("When variable exists") {
|
||||
value->set_variable("compare_var");
|
||||
bb->set_var("compare_var", 123);
|
||||
CHECK(cv->execute(0.01666) == BTTask::SUCCESS);
|
||||
bb->set_var("compare_var", 567);
|
||||
CHECK(cv->execute(0.01666) == BTTask::FAILURE);
|
||||
}
|
||||
SUBCASE("When variable doesn't exist") {
|
||||
value->set_variable("not_found");
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(cv->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
}
|
||||
|
||||
value->set_value_source(BBParam::SAVED_VALUE);
|
||||
|
||||
SUBCASE("With integer") {
|
||||
TC_CHECK_VALUES(cv, 5, 4, "5", LimboUtility::CHECK_EQUAL, 5);
|
||||
TC_CHECK_VALUES(cv, 5, 4, "5", LimboUtility::CHECK_GREATER_THAN_OR_EQUAL, 5);
|
||||
TC_CHECK_VALUES(cv, 6, 4, "6", LimboUtility::CHECK_GREATER_THAN, 5);
|
||||
TC_CHECK_VALUES(cv, 5, 6, "5", LimboUtility::CHECK_LESS_THAN_OR_EQUAL, 5);
|
||||
TC_CHECK_VALUES(cv, 4, 6, "4", LimboUtility::CHECK_LESS_THAN, 5);
|
||||
TC_CHECK_VALUES(cv, 4, 5, "4", LimboUtility::CHECK_NOT_EQUAL, 5);
|
||||
}
|
||||
SUBCASE("With bool") {
|
||||
TC_CHECK_VALUES(cv, true, false, "true", LimboUtility::CHECK_EQUAL, true);
|
||||
TC_CHECK_VALUES(cv, true, false, "true", LimboUtility::CHECK_NOT_EQUAL, false);
|
||||
}
|
||||
SUBCASE("With float") {
|
||||
TC_CHECK_VALUES(cv, 3.14, 3.0, "3.14", LimboUtility::CHECK_EQUAL, 3.14);
|
||||
TC_CHECK_VALUES(cv, 3.14, 3.0, "3.14", LimboUtility::CHECK_GREATER_THAN_OR_EQUAL, 3.14);
|
||||
TC_CHECK_VALUES(cv, 4.0, 3.0, "4.0", LimboUtility::CHECK_GREATER_THAN, 3.14);
|
||||
TC_CHECK_VALUES(cv, 3.14, 4.0, "3.14", LimboUtility::CHECK_LESS_THAN_OR_EQUAL, 3.14);
|
||||
TC_CHECK_VALUES(cv, 3.0, 4.0, "3.0", LimboUtility::CHECK_LESS_THAN, 3.14);
|
||||
TC_CHECK_VALUES(cv, 3.0, 3.14, "3.0", LimboUtility::CHECK_NOT_EQUAL, 3.14);
|
||||
}
|
||||
SUBCASE("With string") {
|
||||
TC_CHECK_VALUES(cv, "AAA", "AAC", 123, LimboUtility::CHECK_EQUAL, "AAA");
|
||||
TC_CHECK_VALUES(cv, "AAC", "AAA", 123, LimboUtility::CHECK_GREATER_THAN_OR_EQUAL, "AAB");
|
||||
TC_CHECK_VALUES(cv, "AAC", "AAA", 123, LimboUtility::CHECK_GREATER_THAN, "AAB");
|
||||
TC_CHECK_VALUES(cv, "AAA", "AAC", 123, LimboUtility::CHECK_LESS_THAN_OR_EQUAL, "AAB");
|
||||
TC_CHECK_VALUES(cv, "AAA", "AAC", 123, LimboUtility::CHECK_LESS_THAN, "AAB");
|
||||
TC_CHECK_VALUES(cv, "AAA", "AAB", 123, LimboUtility::CHECK_NOT_EQUAL, "AAB");
|
||||
}
|
||||
}
|
||||
|
||||
memdelete(dummy);
|
||||
}
|
||||
|
||||
} //namespace TestCheckVar
|
||||
|
||||
#endif // TEST_CHECK_VAR_H
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* test_delay.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_DELAY_H
|
||||
#define TEST_DELAY_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/decorators/bt_delay.h"
|
||||
|
||||
namespace TestDelay {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTDelay") {
|
||||
Ref<BTDelay> del = memnew(BTDelay);
|
||||
|
||||
SUBCASE("When empty") {
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(del->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
Ref<BTTestAction> task = memnew(BTTestAction(BTTask::SUCCESS));
|
||||
del->add_child(task);
|
||||
|
||||
SUBCASE("Check if delay is observed correctly") {
|
||||
del->set_seconds(1.0);
|
||||
CHECK(del->execute(0.35) == BTTask::RUNNING); // * first execution: elapsed 0.0
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 0, 0, 0);
|
||||
CHECK(del->execute(0.35) == BTTask::RUNNING); // * second execution: elapsed 0.35
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 0, 0, 0);
|
||||
CHECK(del->execute(0.35) == BTTask::RUNNING); // * third execution: elapsed 0.7
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 0, 0, 0);
|
||||
|
||||
SUBCASE("When child task returns SUCCESS") {
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(del->execute(0.35) == BTTask::SUCCESS); // * fourth execution: elapsed 1.05
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 1);
|
||||
}
|
||||
SUBCASE("When child task returns FAILURE") {
|
||||
task->ret_status = BTTask::FAILURE;
|
||||
CHECK(del->execute(0.35) == BTTask::FAILURE); // * fourth execution: elapsed 1.05
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 1);
|
||||
}
|
||||
SUBCASE("When child task returns RUNNING") {
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
CHECK(del->execute(0.35) == BTTask::RUNNING); // * fourth execution: elapsed 1.05
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 0);
|
||||
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(del->execute(0.35) == BTTask::SUCCESS); // * fifth execution: elapsed 1.4
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 2, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestDelay
|
||||
|
||||
#endif // TEST_DELAY_H
|
|
@ -0,0 +1,121 @@
|
|||
/**
|
||||
* test_dynamic_selector.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_DYNAMIC_SELECTOR_H
|
||||
#define TEST_DYNAMIC_SELECTOR_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/composites/bt_dynamic_selector.h"
|
||||
|
||||
namespace TestDynamicSelector {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTDynamicSelector") {
|
||||
Ref<BTDynamicSelector> sel = memnew(BTDynamicSelector);
|
||||
Ref<BTTestAction> task1 = memnew(BTTestAction());
|
||||
Ref<BTTestAction> task2 = memnew(BTTestAction());
|
||||
Ref<BTTestAction> task3 = memnew(BTTestAction());
|
||||
|
||||
sel->add_child(task1);
|
||||
sel->add_child(task2);
|
||||
sel->add_child(task3);
|
||||
|
||||
REQUIRE(sel->get_child_count() == 3);
|
||||
|
||||
SUBCASE("Subcase #1: Dynamic selector processes tasks sequentially until first SUCCESS, while re-evaluating its child tasks in every execution tick.") {
|
||||
task1->ret_status = BTTask::FAILURE;
|
||||
task2->ret_status = BTTask::RUNNING;
|
||||
task3->ret_status = BTTask::SUCCESS;
|
||||
|
||||
CHECK(sel->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::FAILURE);
|
||||
CHECK(task2->get_status() == BTTask::RUNNING);
|
||||
CHECK(task3->get_status() == BTTask::FRESH);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1); // * finished with FAILURE
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 0); // * enetered and running
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 0, 0, 0); // * still fresh
|
||||
|
||||
SUBCASE("Subcase 1A: With no changes, first task is re-evaluated.") {
|
||||
CHECK(sel->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::FAILURE);
|
||||
CHECK(task2->get_status() == BTTask::RUNNING);
|
||||
CHECK(task3->get_status() == BTTask::FRESH);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 2, 2, 2); // * re-evaluated with FAILURE
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 2, 0); // * continued
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 0, 0, 0); // * still fresh
|
||||
}
|
||||
|
||||
SUBCASE("Subcase 1B: When second task succeeds, we finish with SUCCESS.") {
|
||||
task2->ret_status = BTTask::SUCCESS;
|
||||
CHECK(sel->execute(0.01666) == BTTask::SUCCESS);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::FAILURE);
|
||||
CHECK(task2->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task3->get_status() == BTTask::FRESH);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 2, 2, 2); // * re-evaluated with FAILURE
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 2, 1); // * ticked and exited with SUCCESS
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 0, 0, 0); // * still fresh
|
||||
}
|
||||
|
||||
SUBCASE("Subcase 1C: When first task re-evaluates to SUCCESS, second task should be cancelled and exited.") {
|
||||
task1->ret_status = BTTask::SUCCESS;
|
||||
CHECK(sel->execute(0.01666) == BTTask::SUCCESS);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::FRESH); // * cancelled - status changed to FRESH
|
||||
CHECK(task3->get_status() == BTTask::FRESH);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 2, 2, 2); // * re-evaluated with SUCCESS
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 1); // * cancelled - not ticked and exited
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 0, 0, 0); // * still fresh
|
||||
}
|
||||
|
||||
SUBCASE("Subcase 1D: When first two fail, third one is executed.") {
|
||||
task1->ret_status = BTTask::FAILURE;
|
||||
task2->ret_status = BTTask::FAILURE;
|
||||
task3->ret_status = BTTask::RUNNING;
|
||||
CHECK(sel->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::FAILURE);
|
||||
CHECK(task2->get_status() == BTTask::FAILURE);
|
||||
CHECK(task3->get_status() == BTTask::RUNNING);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 2, 2, 2); // * re-evaluated with FAILURE
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 2, 1); // * ticked and exited with FAILURE
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 1, 0); // * entered and running
|
||||
|
||||
SUBCASE("Subcase 1D1: First two are re-evaluated, and when all finish with FAILURE, we expect FAILURE.") {
|
||||
task1->ret_status = BTTask::FAILURE;
|
||||
task2->ret_status = BTTask::FAILURE;
|
||||
task3->ret_status = BTTask::FAILURE;
|
||||
CHECK(sel->execute(0.01666) == BTTask::FAILURE);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::FAILURE);
|
||||
CHECK(task2->get_status() == BTTask::FAILURE);
|
||||
CHECK(task3->get_status() == BTTask::FAILURE);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 3, 3, 3); // * re-evaluated with SUCCESS
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 2, 3, 2); // * re-evaluated with SUCCESS
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 2, 1); // * ticked and exited with SUCCESS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestDynamicSelector
|
||||
|
||||
#endif // TEST_DYNAMIC_SELECTOR_H
|
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* test_dynamic_sequence.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_DYNAMIC_SEQUENCE_H
|
||||
#define TEST_DYNAMIC_SEQUENCE_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/composites/bt_dynamic_sequence.h"
|
||||
|
||||
namespace TestDynamicSequence {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTDynamicSequence") {
|
||||
Ref<BTDynamicSequence> seq = memnew(BTDynamicSequence);
|
||||
Ref<BTTestAction> task1 = memnew(BTTestAction());
|
||||
Ref<BTTestAction> task2 = memnew(BTTestAction());
|
||||
Ref<BTTestAction> task3 = memnew(BTTestAction());
|
||||
|
||||
seq->add_child(task1);
|
||||
seq->add_child(task2);
|
||||
seq->add_child(task3);
|
||||
|
||||
REQUIRE(seq->get_child_count() == 3);
|
||||
|
||||
SUBCASE("Subcase #1: Dynamic sequence processes tasks sequentially, while re-evaluating its child tasks in every execution tick.") {
|
||||
task1->ret_status = BTTask::SUCCESS;
|
||||
task2->ret_status = BTTask::RUNNING;
|
||||
task3->ret_status = BTTask::SUCCESS;
|
||||
|
||||
CHECK(seq->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::RUNNING);
|
||||
CHECK(task3->get_status() == BTTask::FRESH);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1); // * finished
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 0); // * running
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 0, 0, 0); // * still fresh
|
||||
|
||||
SUBCASE("Subcase 1A: With no changes, first task is re-evaluated.") {
|
||||
CHECK(seq->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::RUNNING);
|
||||
CHECK(task3->get_status() == BTTask::FRESH);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 2, 2, 2); // * re-evaluated with SUCCESS
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 2, 0); // * continued
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 0, 0, 0); // * still fresh
|
||||
}
|
||||
|
||||
SUBCASE("Subcase 1B: When first task re-evaluates to FAILURE, second task should be cancelled and exited.") {
|
||||
task1->ret_status = BTTask::FAILURE;
|
||||
CHECK(seq->execute(0.01666) == BTTask::FAILURE);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::FAILURE);
|
||||
CHECK(task2->get_status() == BTTask::FRESH); // * cancelled - status changed to FRESH
|
||||
CHECK(task3->get_status() == BTTask::FRESH);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 2, 2, 2); // * re-evaluated with FAILURE
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 1); // * cancelled - not ticked and exited
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 0, 0, 0); // * still fresh
|
||||
}
|
||||
|
||||
SUBCASE("Subcase 1C: When second task finished, third one is executed.") {
|
||||
task1->ret_status = BTTask::SUCCESS;
|
||||
task2->ret_status = BTTask::SUCCESS;
|
||||
task3->ret_status = BTTask::RUNNING;
|
||||
CHECK(seq->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task3->get_status() == BTTask::RUNNING);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 2, 2, 2); // * re-evaluated with SUCCESS
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 2, 1); // * ticked and exited with SUCCESS
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 1, 0); // * entered and running
|
||||
|
||||
SUBCASE("Subcase 1C1: First two are re-evaluated, and when all finish with SUCCESS, we expect SUCCESS.") {
|
||||
task1->ret_status = BTTask::SUCCESS;
|
||||
task2->ret_status = BTTask::SUCCESS;
|
||||
task3->ret_status = BTTask::SUCCESS;
|
||||
CHECK(seq->execute(0.01666) == BTTask::SUCCESS);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task3->get_status() == BTTask::SUCCESS);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 3, 3, 3); // * re-evaluated with SUCCESS
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 2, 3, 2); // * re-evaluated with SUCCESS
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 2, 1); // * ticked and exited with SUCCESS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestDynamicSequence
|
||||
|
||||
#endif // TEST_DYNAMIC_SEQUENCE_H
|
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* test_for_each.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_FOR_EACH_H
|
||||
#define TEST_FOR_EACH_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/decorators/bt_for_each.h"
|
||||
|
||||
namespace TestForEach {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTForEach") {
|
||||
Ref<BTForEach> fe = memnew(BTForEach);
|
||||
Node *dummy = memnew(Node);
|
||||
Ref<Blackboard> blackboard = memnew(Blackboard);
|
||||
fe->initialize(dummy, blackboard);
|
||||
|
||||
Array arr;
|
||||
arr.append("apple");
|
||||
arr.append("raspberry");
|
||||
arr.append("mushroom");
|
||||
blackboard->set_var("array", arr);
|
||||
|
||||
SUBCASE("When empty") {
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(fe->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
Ref<BTTestAction> task = memnew(BTTestAction(BTTask::SUCCESS));
|
||||
fe->add_child(task);
|
||||
fe->set_array_var("array");
|
||||
fe->set_save_var("element");
|
||||
|
||||
SUBCASE("When child returns SUCCESS") {
|
||||
CHECK(fe->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK(task->get_status() == BTTask::SUCCESS);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 1);
|
||||
CHECK(blackboard->get_var("element", "wetgoop") == "apple");
|
||||
|
||||
CHECK(fe->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK(task->get_status() == BTTask::SUCCESS);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 2, 2, 2);
|
||||
CHECK(blackboard->get_var("element", "wetgoop") == "raspberry");
|
||||
|
||||
CHECK(fe->execute(0.01666) == BTTask::SUCCESS); // * finished iterating - returning SUCCESS
|
||||
CHECK(task->get_status() == BTTask::SUCCESS);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 3, 3, 3);
|
||||
CHECK(blackboard->get_var("element", "wetgoop") == "mushroom");
|
||||
}
|
||||
|
||||
SUBCASE("When child task takes more than one tick to finish") {
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
CHECK(fe->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK(task->get_status() == BTTask::RUNNING);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 0);
|
||||
CHECK(blackboard->get_var("element", "wetgoop") == "apple");
|
||||
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(fe->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK(task->get_status() == BTTask::SUCCESS);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 2, 1);
|
||||
CHECK(blackboard->get_var("element", "wetgoop") == "apple");
|
||||
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
CHECK(fe->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK(task->get_status() == BTTask::RUNNING);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 2, 3, 1);
|
||||
CHECK(blackboard->get_var("element", "wetgoop") == "raspberry");
|
||||
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(fe->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK(task->get_status() == BTTask::SUCCESS);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 2, 4, 2);
|
||||
CHECK(blackboard->get_var("element", "wetgoop") == "raspberry");
|
||||
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
CHECK(fe->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK(task->get_status() == BTTask::RUNNING);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 3, 5, 2);
|
||||
CHECK(blackboard->get_var("element", "wetgoop") == "mushroom");
|
||||
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(fe->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK(task->get_status() == BTTask::SUCCESS);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 3, 6, 3);
|
||||
CHECK(blackboard->get_var("element", "wetgoop") == "mushroom");
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestForEach
|
||||
|
||||
#endif // TEST_FOR_EACH_H
|
|
@ -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
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* test_invert.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_INVERT_H
|
||||
#define TEST_INVERT_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/decorators/bt_invert.h"
|
||||
|
||||
namespace TestInvert {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTInvert") {
|
||||
Ref<BTInvert> inv = memnew(BTInvert);
|
||||
|
||||
SUBCASE("When empty") {
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(inv->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
Ref<BTTestAction> task = memnew(BTTestAction);
|
||||
inv->add_child(task);
|
||||
|
||||
SUBCASE("With SUCCESS") {
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(inv->execute(0.01666) == BTTask::FAILURE);
|
||||
CHECK(task->get_status() == BTTask::SUCCESS);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 1);
|
||||
}
|
||||
|
||||
SUBCASE("With FAILURE") {
|
||||
task->ret_status = BTTask::FAILURE;
|
||||
CHECK(inv->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK(task->get_status() == BTTask::FAILURE);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 1);
|
||||
}
|
||||
|
||||
SUBCASE("With RUNNING followed by SUCCESS") {
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
CHECK(inv->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK(task->get_status() == BTTask::RUNNING);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 0);
|
||||
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(inv->execute(0.01666) == BTTask::FAILURE);
|
||||
CHECK(task->get_status() == BTTask::SUCCESS);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 1, 2, 1);
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestInvert
|
||||
|
||||
#endif // TEST_INVERT_H
|
|
@ -0,0 +1,100 @@
|
|||
/**
|
||||
* test_new_scope.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_NEW_SCOPE_H
|
||||
#define TEST_NEW_SCOPE_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/decorators/bt_new_scope.h"
|
||||
|
||||
namespace TestNewScope {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTNewScope") {
|
||||
Ref<BTNewScope> ns = memnew(BTNewScope);
|
||||
Node *dummy = memnew(Node);
|
||||
Ref<Blackboard> parent_bb = memnew(Blackboard);
|
||||
|
||||
SUBCASE("When empty") {
|
||||
ERR_PRINT_OFF;
|
||||
ns->initialize(dummy, parent_bb);
|
||||
CHECK(ns->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
SUBCASE("When not empty") {
|
||||
Ref<BTTask> parent = memnew(BTTask);
|
||||
parent->add_child(ns);
|
||||
|
||||
Ref<BTTestAction> child = memnew(BTTestAction);
|
||||
ns->add_child(child);
|
||||
|
||||
parent_bb->set_var("fruit", "apple");
|
||||
parent_bb->set_var("vegetable", "carrot");
|
||||
REQUIRE(parent_bb->has_var("fruit"));
|
||||
REQUIRE(parent_bb->get_var("fruit", "wetgoop") == "apple");
|
||||
REQUIRE(parent_bb->has_var("vegetable"));
|
||||
REQUIRE(parent_bb->get_var("vegetable", "wetgoop") == "carrot");
|
||||
|
||||
parent->initialize(dummy, parent_bb);
|
||||
|
||||
CHECK(ns->get_blackboard() != parent->get_blackboard());
|
||||
CHECK(ns->get_blackboard() == child->get_blackboard());
|
||||
CHECK(parent->get_blackboard() == parent_bb);
|
||||
CHECK(ns->get_blackboard()->get_parent_scope() == parent_bb);
|
||||
|
||||
ns->get_blackboard()->set_var("fruit", "pear"); // * override "fruit"
|
||||
|
||||
CHECK(ns->get_blackboard()->get_var("fruit", "wetgoop") == "pear");
|
||||
CHECK(child->get_blackboard()->get_var("fruit", "wetgoop") == "pear");
|
||||
CHECK(parent->get_blackboard()->get_var("fruit", "wetgoop") == "apple");
|
||||
|
||||
// * Check if new scope inherits "vegetable"
|
||||
CHECK(ns->get_blackboard()->has_var("vegetable"));
|
||||
CHECK(ns->get_blackboard()->get_var("vegetable", "wetgoop") == "carrot");
|
||||
CHECK(child->get_blackboard()->get_var("vegetable", "wetgoop") == "carrot");
|
||||
|
||||
// * Check if "vegetable" from the parent scope is accessible
|
||||
CHECK(ns->get_blackboard()->has_var("vegetable"));
|
||||
CHECK(child->get_blackboard()->has_var("vegetable"));
|
||||
CHECK(ns->get_blackboard()->get_var("vegetable", "wetgoop") == "carrot");
|
||||
CHECK(child->get_blackboard()->get_var("vegetable", "wetgoop") == "carrot");
|
||||
|
||||
// * Check if setting a variable doesn't propagate it up the scope
|
||||
ns->get_blackboard()->set_var("berry", "raspberry");
|
||||
CHECK(ns->get_blackboard()->get_var("berry", "wetgoop") == "raspberry");
|
||||
CHECK(child->get_blackboard()->get_var("berry", "wetgoop") == "raspberry");
|
||||
CHECK(parent->get_blackboard()->get_var("berry", "wetgoop") == "wetgoop");
|
||||
CHECK_FALSE(parent->get_blackboard()->has_var("berry"));
|
||||
|
||||
// * Check if setting a variable doesn't propagate it up the scope (now with the child task)
|
||||
child->get_blackboard()->set_var("seed", "sunflower");
|
||||
CHECK(child->get_blackboard()->get_var("seed", "wetgoop") == "sunflower");
|
||||
CHECK(ns->get_blackboard()->get_var("seed", "wetgoop") == "sunflower");
|
||||
CHECK(parent->get_blackboard()->get_var("seed", "wetgoop") == "wetgoop");
|
||||
CHECK_FALSE(parent->get_blackboard()->has_var("seed"));
|
||||
|
||||
// * Check return status
|
||||
child->ret_status = BTTask::SUCCESS;
|
||||
CHECK(ns->execute(0.01666) == BTTask::SUCCESS);
|
||||
child->ret_status = BTTask::FAILURE;
|
||||
CHECK(ns->execute(0.01666) == BTTask::FAILURE);
|
||||
child->ret_status = BTTask::RUNNING;
|
||||
CHECK(ns->execute(0.01666) == BTTask::RUNNING);
|
||||
}
|
||||
|
||||
memdelete(dummy);
|
||||
}
|
||||
|
||||
} //namespace TestNewScope
|
||||
|
||||
#endif // TEST_NEW_SCOPE_H
|
|
@ -0,0 +1,212 @@
|
|||
/**
|
||||
* test_parallel.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_PARALLEL_H
|
||||
#define TEST_PARALLEL_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/composites/bt_parallel.h"
|
||||
|
||||
namespace TestParallel {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTParallel with num_required_successes: 1 and num_required_failures: 1") {
|
||||
Ref<BTParallel> par = memnew(BTParallel);
|
||||
Ref<BTTestAction> task1 = memnew(BTTestAction());
|
||||
Ref<BTTestAction> task2 = memnew(BTTestAction());
|
||||
Ref<BTTestAction> task3 = memnew(BTTestAction());
|
||||
|
||||
par->add_child(task1);
|
||||
par->add_child(task2);
|
||||
par->add_child(task3);
|
||||
|
||||
REQUIRE(par->get_child_count() == 3);
|
||||
|
||||
SUBCASE("BTParallel composition {RUNNING, SUCCESS, FAILURE} and successes/failures required 1/1") {
|
||||
// * Case #1: When reached both success and failure required, we expect one that triggered sooner (SUCCESS in this case).
|
||||
task1->ret_status = BTTask::RUNNING;
|
||||
task2->ret_status = BTTask::SUCCESS;
|
||||
task3->ret_status = BTTask::FAILURE;
|
||||
par->set_num_successes_required(1);
|
||||
par->set_num_failures_required(1);
|
||||
par->set_repeat(false);
|
||||
|
||||
CHECK(par->execute(0.01666) == BTTask::SUCCESS); // When reached both conditions.
|
||||
|
||||
CHECK(task1->get_status() == BTTask::RUNNING);
|
||||
CHECK(task2->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task3->get_status() == BTTask::FAILURE);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 0); // * running
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 1); // * finished
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 1, 1); // * finished
|
||||
}
|
||||
|
||||
SUBCASE("BTParallel composition {RUNNING, SUCCESS, RUNNING} and successes/failures required 1/1") {
|
||||
// * Case #1b: When reached required number of successes, we expect SUCCESS.
|
||||
task1->ret_status = BTTask::RUNNING;
|
||||
task2->ret_status = BTTask::SUCCESS;
|
||||
task3->ret_status = BTTask::RUNNING;
|
||||
par->set_num_successes_required(1);
|
||||
par->set_num_failures_required(1);
|
||||
par->set_repeat(false);
|
||||
|
||||
CHECK(par->execute(0.01666) == BTTask::SUCCESS);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::RUNNING);
|
||||
CHECK(task2->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task3->get_status() == BTTask::RUNNING);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 0); // * running
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 1); // * finished
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 1, 0); // * running
|
||||
}
|
||||
|
||||
SUBCASE("BTParallel composition {RUNNING, FAILURE, RUNNING} and successes/failures required 1/1") {
|
||||
// * Case #1c: When reached required number of failures, we expect FAILURE.
|
||||
task1->ret_status = BTTask::RUNNING;
|
||||
task2->ret_status = BTTask::FAILURE;
|
||||
task3->ret_status = BTTask::RUNNING;
|
||||
par->set_num_successes_required(1);
|
||||
par->set_num_failures_required(1);
|
||||
par->set_repeat(false);
|
||||
|
||||
CHECK(par->execute(0.01666) == BTTask::FAILURE);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::RUNNING);
|
||||
CHECK(task2->get_status() == BTTask::FAILURE);
|
||||
CHECK(task3->get_status() == BTTask::RUNNING);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 0); // * running
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 1); // * finished
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 1, 0); // * running
|
||||
}
|
||||
|
||||
SUBCASE("BTParallel composition {SUCCESS, RUNNING, FAILURE} with successes/failures required 3/3 (not repeating)") {
|
||||
// * Case #2: When failed to reach required number of successes or failures,
|
||||
// * and not all children finished executing while not repeating, we expect RUNNING.
|
||||
task1->ret_status = BTTask::SUCCESS;
|
||||
task2->ret_status = BTTask::RUNNING;
|
||||
task3->ret_status = BTTask::FAILURE;
|
||||
par->set_num_successes_required(3);
|
||||
par->set_num_failures_required(3);
|
||||
par->set_repeat(false);
|
||||
|
||||
CHECK(par->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::RUNNING);
|
||||
CHECK(task3->get_status() == BTTask::FAILURE);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1); // * finished
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 0); // * running
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 1, 1); // * finished
|
||||
}
|
||||
|
||||
SUBCASE("BTParallel composition {SUCCESS, FAILURE, SUCCESS} with successes/failures required 3/3 (not repeating)") {
|
||||
// * Case #3: When failed to reach required number of successes or failures,
|
||||
// * and all children finished executing while not repeating, we expect FAILURE.
|
||||
task1->ret_status = BTTask::SUCCESS;
|
||||
task2->ret_status = BTTask::FAILURE;
|
||||
task3->ret_status = BTTask::SUCCESS;
|
||||
par->set_num_successes_required(3);
|
||||
par->set_num_failures_required(3);
|
||||
par->set_repeat(false);
|
||||
CHECK(par->execute(0.01666) == BTTask::FAILURE);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::FAILURE);
|
||||
CHECK(task3->get_status() == BTTask::SUCCESS);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 1, 1);
|
||||
}
|
||||
|
||||
SUBCASE("BTParallel composition {SUCCESS, FAILURE, SUCCESS} with successes/failures required 3/3 (repeating)") {
|
||||
// * Case #4: When failed to reach required number of successes or failures,
|
||||
// * and all children finished executing while repeating, we expect RUNNING.
|
||||
task1->ret_status = BTTask::SUCCESS;
|
||||
task2->ret_status = BTTask::FAILURE;
|
||||
task3->ret_status = BTTask::SUCCESS;
|
||||
par->set_num_successes_required(3);
|
||||
par->set_num_failures_required(3);
|
||||
par->set_repeat(true);
|
||||
CHECK(par->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::FAILURE);
|
||||
CHECK(task3->get_status() == BTTask::SUCCESS);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 1, 1);
|
||||
|
||||
// * Execution #2: Check if tasks are repeated, when set so (there is no RUNNING task).
|
||||
CHECK(par->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::FAILURE);
|
||||
CHECK(task3->get_status() == BTTask::SUCCESS);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 2, 2, 2); // * repeated
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 2, 2, 2); // * repeated
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 2, 2, 2); // * repeated
|
||||
}
|
||||
|
||||
SUBCASE("BTParallel composition {SUCCESS, RUNNING, FAILURE} with successes/failures required 2/2 (not repeating)") {
|
||||
// * Case #5: When failed to reach required number of successes or failures,
|
||||
// * but not all children finished executing (not repeating), we expect RUNNING.
|
||||
task1->ret_status = BTTask::SUCCESS;
|
||||
task2->ret_status = BTTask::RUNNING;
|
||||
task3->ret_status = BTTask::FAILURE;
|
||||
par->set_num_successes_required(2);
|
||||
par->set_num_failures_required(2);
|
||||
par->set_repeat(false);
|
||||
CHECK(par->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::RUNNING);
|
||||
CHECK(task3->get_status() == BTTask::FAILURE);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 0);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 1, 1);
|
||||
|
||||
// * Execution #2: Check if tasks are not repeated, when set so.
|
||||
CHECK(par->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::RUNNING);
|
||||
CHECK(task3->get_status() == BTTask::FAILURE);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1); // * not repeated
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 2, 0); // * continued
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 1, 1); // * not repeated
|
||||
|
||||
// * Execution #3: Check if tasks are repeated, when set so.
|
||||
par->set_repeat(true);
|
||||
CHECK(par->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::RUNNING);
|
||||
CHECK(task3->get_status() == BTTask::FAILURE);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 2, 2, 2); // * repeated
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 3, 0); // * continued
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 2, 2, 2); // * repeated
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestParallel
|
||||
|
||||
#endif // TEST_PARALLEL_H
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* test_pause_animation.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_PAUSE_ANIMATION_H
|
||||
#define TEST_PAUSE_ANIMATION_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/scene/bt_pause_animation.h"
|
||||
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/resources/animation.h"
|
||||
#include "scene/resources/animation_library.h"
|
||||
|
||||
namespace TestPauseAnimation {
|
||||
|
||||
TEST_CASE("[SceneTree][LimboAI] BTPauseAnimation") {
|
||||
AnimationPlayer *player = memnew(AnimationPlayer);
|
||||
SceneTree::get_singleton()->get_root()->add_child(player);
|
||||
player->set_process_callback(AnimationPlayer::AnimationProcessCallback::ANIMATION_PROCESS_IDLE);
|
||||
|
||||
Ref<AnimationLibrary> anim_lib = memnew(AnimationLibrary);
|
||||
Ref<Animation> anim = memnew(Animation);
|
||||
anim->set_name("test");
|
||||
anim->set_length(0.1);
|
||||
anim->set_loop_mode(Animation::LOOP_NONE);
|
||||
REQUIRE(anim_lib->add_animation("test", anim) == OK);
|
||||
REQUIRE(player->add_animation_library("", anim_lib) == OK);
|
||||
REQUIRE(player->has_animation("test"));
|
||||
|
||||
Ref<BTPauseAnimation> pa = memnew(BTPauseAnimation);
|
||||
Ref<BBNode> player_param = memnew(BBNode);
|
||||
pa->set_animation_player(player_param);
|
||||
Node *dummy = memnew(Node);
|
||||
SceneTree::get_singleton()->get_root()->add_child(dummy);
|
||||
Ref<Blackboard> bb = memnew(Blackboard);
|
||||
|
||||
SUBCASE("When AnimationPlayer doesn't exist") {
|
||||
player_param->set_saved_value(NodePath("./NotFound"));
|
||||
ERR_PRINT_OFF;
|
||||
pa->initialize(dummy, bb);
|
||||
CHECK(pa->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("When AnimationPlayer exists") {
|
||||
player_param->set_saved_value(player->get_path());
|
||||
pa->initialize(dummy, bb);
|
||||
|
||||
SUBCASE("When AnimationPlayer is not playing") {
|
||||
REQUIRE_FALSE(player->is_playing());
|
||||
CHECK(pa->execute(0.01666) == BTTask::SUCCESS);
|
||||
}
|
||||
SUBCASE("When AnimationPlayer is playing") {
|
||||
player->play("test");
|
||||
REQUIRE(player->is_playing());
|
||||
CHECK(pa->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK_FALSE(player->is_playing());
|
||||
}
|
||||
}
|
||||
|
||||
memdelete(dummy);
|
||||
memdelete(player);
|
||||
}
|
||||
|
||||
} //namespace TestPauseAnimation
|
||||
|
||||
#endif // TEST_PAUSE_ANIMATION_H
|
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* test_play_animation.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_PLAY_ANIMATION_H
|
||||
#define TEST_PLAY_ANIMATION_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/scene/bt_play_animation.h"
|
||||
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/resources/animation.h"
|
||||
#include "scene/resources/animation_library.h"
|
||||
|
||||
namespace TestPlayAnimation {
|
||||
|
||||
TEST_CASE("[SceneTree][LimboAI] BTPlayAnimation") {
|
||||
AnimationPlayer *player = memnew(AnimationPlayer);
|
||||
SceneTree::get_singleton()->get_root()->add_child(player);
|
||||
player->set_process_callback(AnimationPlayer::AnimationProcessCallback::ANIMATION_PROCESS_IDLE);
|
||||
|
||||
Ref<AnimationLibrary> anim_lib = memnew(AnimationLibrary);
|
||||
Ref<Animation> anim = memnew(Animation);
|
||||
anim->set_name("test");
|
||||
anim->set_length(0.1);
|
||||
anim->set_loop_mode(Animation::LOOP_NONE);
|
||||
REQUIRE(anim_lib->add_animation("test", anim) == OK);
|
||||
REQUIRE(player->add_animation_library("", anim_lib) == OK);
|
||||
REQUIRE(player->has_animation("test"));
|
||||
|
||||
Ref<BTPlayAnimation> pa = memnew(BTPlayAnimation);
|
||||
pa->set_animation_name("test");
|
||||
Ref<BBNode> player_param = memnew(BBNode);
|
||||
pa->set_animation_player(player_param);
|
||||
Node *dummy = memnew(Node);
|
||||
SceneTree::get_singleton()->get_root()->add_child(dummy);
|
||||
Ref<Blackboard> bb = memnew(Blackboard);
|
||||
|
||||
SUBCASE("When AnimationPlayer doesn't exist") {
|
||||
player_param->set_saved_value(NodePath("./NotFound"));
|
||||
ERR_PRINT_OFF;
|
||||
pa->initialize(dummy, bb);
|
||||
CHECK(pa->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("When AnimationPlayer exists") {
|
||||
player_param->set_saved_value(player->get_path());
|
||||
pa->initialize(dummy, bb);
|
||||
|
||||
SUBCASE("When not waiting to finish") {
|
||||
pa->set_await_completion(0.0);
|
||||
CHECK(pa->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK(player->is_playing());
|
||||
CHECK(player->get_current_animation() == "test");
|
||||
}
|
||||
SUBCASE("When exceeding max wait time") {
|
||||
pa->set_await_completion(1.0);
|
||||
CHECK(pa->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK(player->is_playing());
|
||||
CHECK(player->get_current_animation() == "test");
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(pa->execute(1.0) == BTTask::SUCCESS);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("When animation finishes playing before wait time runs out") {
|
||||
pa->set_await_completion(888.0);
|
||||
CHECK(pa->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK(player->is_playing());
|
||||
CHECK(player->get_current_animation() == "test");
|
||||
|
||||
player->seek(888.0, true);
|
||||
player->notification(Node::NOTIFICATION_INTERNAL_PROCESS);
|
||||
CHECK_FALSE(player->is_playing());
|
||||
CHECK_FALSE(player->get_current_animation() == "test");
|
||||
CHECK(pa->execute(0.01666) == BTTask::SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
memdelete(dummy);
|
||||
memdelete(player);
|
||||
}
|
||||
|
||||
} //namespace TestPlayAnimation
|
||||
|
||||
#endif // TEST_PLAY_ANIMATION_H
|
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* test_probability.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_PROBABILITY_H
|
||||
#define TEST_PROBABILITY_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/decorators/bt_probability.h"
|
||||
|
||||
#include "core/math/math_funcs.h"
|
||||
|
||||
namespace TestProbability {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTProbability") {
|
||||
Ref<BTProbability> prob = memnew(BTProbability);
|
||||
|
||||
SUBCASE("When empty") {
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(prob->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
Ref<BTTestAction> task = memnew(BTTestAction);
|
||||
prob->add_child(task);
|
||||
|
||||
Math::randomize();
|
||||
|
||||
SUBCASE("Check if probability meets expectation") {
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
prob->set_run_chance(0.5);
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
prob->execute(0.01666);
|
||||
}
|
||||
|
||||
CHECK(task->num_ticks > 450);
|
||||
CHECK(task->num_ticks < 550);
|
||||
}
|
||||
|
||||
SUBCASE("When probability is 0") {
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
prob->set_run_chance(0.0);
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
prob->execute(0.01666);
|
||||
}
|
||||
|
||||
CHECK(task->num_ticks == 0);
|
||||
}
|
||||
|
||||
SUBCASE("When probability is 1") {
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
prob->set_run_chance(1.0);
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
prob->execute(0.01666);
|
||||
}
|
||||
|
||||
CHECK(task->num_ticks == 1000);
|
||||
}
|
||||
|
||||
SUBCASE("Test return status") {
|
||||
prob->set_run_chance(1.0);
|
||||
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(prob->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 1, 1, 1);
|
||||
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
CHECK(prob->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::RUNNING, 2, 2, 1);
|
||||
|
||||
task->ret_status = BTTask::FAILURE;
|
||||
CHECK(prob->execute(0.01666) == BTTask::FAILURE);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::FAILURE, 2, 3, 2);
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestProbability
|
||||
|
||||
#endif // TEST_PROBABILITY_H
|
|
@ -0,0 +1,110 @@
|
|||
/**
|
||||
* test_random_selector.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_RANDOM_SELECTOR_H
|
||||
#define TEST_RANDOM_SELECTOR_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/composites/bt_random_selector.h"
|
||||
|
||||
namespace TestRandomSelector {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTRandomSelector") {
|
||||
Ref<BTRandomSelector> sel = memnew(BTRandomSelector);
|
||||
Ref<BTTestAction> task1 = memnew(BTTestAction());
|
||||
Ref<BTTestAction> task2 = memnew(BTTestAction());
|
||||
Ref<BTTestAction> task3 = memnew(BTTestAction());
|
||||
|
||||
sel->add_child(task1);
|
||||
sel->add_child(task2);
|
||||
sel->add_child(task3);
|
||||
|
||||
REQUIRE(sel->get_child_count() == 3);
|
||||
|
||||
SUBCASE("Expecting RUNNING status when a child task returns RUNNING") {
|
||||
task1->ret_status = BTTask::FAILURE;
|
||||
task2->ret_status = BTTask::RUNNING;
|
||||
task3->ret_status = BTTask::FAILURE;
|
||||
|
||||
CHECK(sel->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->is_status_either(BTTask::FAILURE, BTTask::FRESH));
|
||||
CHECK(task2->get_status() == BTTask::RUNNING);
|
||||
CHECK(task3->is_status_either(BTTask::FAILURE, BTTask::FRESH));
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS_UP_TO(task1, 1, 1, 1); // * ran no more than once
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 0); // * running - enters and ticks
|
||||
CHECK_ENTRIES_TICKS_EXITS_UP_TO(task3, 1, 1, 1); // * ran no more than once
|
||||
|
||||
SUBCASE("Resuming and failing when all tasks fail") {
|
||||
task2->ret_status = BTTask::FAILURE;
|
||||
|
||||
CHECK(sel->execute(0.01666) == BTTask::FAILURE);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::FAILURE);
|
||||
CHECK(task2->get_status() == BTTask::FAILURE);
|
||||
CHECK(task3->get_status() == BTTask::FAILURE);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1); // * ran once
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 2, 1); // * finishes - ticks and exits with FAILURE
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 1, 1); // * ran once
|
||||
}
|
||||
|
||||
SUBCASE("Resuming and succeeding when a child task succeeds") {
|
||||
task2->ret_status = BTTask::SUCCESS;
|
||||
|
||||
CHECK(sel->execute(0.01666) == BTTask::SUCCESS);
|
||||
|
||||
CHECK(task1->is_status_either(BTTask::FAILURE, BTTask::FRESH));
|
||||
CHECK(task2->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task3->is_status_either(BTTask::FAILURE, BTTask::FRESH));
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS_UP_TO(task1, 1, 1, 1); // * ran no more than once
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 2, 1); // * finishes - ticks and exits with SUCCESS
|
||||
CHECK_ENTRIES_TICKS_EXITS_UP_TO(task3, 1, 1, 1); // * ran no more than once
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Verify that tasks are executed in random order") {
|
||||
task1->ret_status = BTTask::FAILURE;
|
||||
task2->ret_status = BTTask::FAILURE;
|
||||
task3->ret_status = BTTask::RUNNING;
|
||||
|
||||
int num_tries = 10;
|
||||
bool is_confirmed = false;
|
||||
while (!is_confirmed && num_tries--) {
|
||||
CHECK(sel->execute(0.01666) == BTTask::RUNNING);
|
||||
int checksum = 0;
|
||||
if (task1->get_status() == BTTask::FAILURE) {
|
||||
checksum += 1;
|
||||
}
|
||||
if (task2->get_status() == BTTask::FAILURE) {
|
||||
checksum += 2;
|
||||
}
|
||||
if (task3->get_status() == BTTask::RUNNING) {
|
||||
checksum += 4;
|
||||
}
|
||||
is_confirmed = (checksum != 7);
|
||||
}
|
||||
CHECK(is_confirmed);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] Empty BTRandomSelector returns FAILURE") {
|
||||
Ref<BTRandomSelector> seq = memnew(BTRandomSelector);
|
||||
CHECK(seq->execute(0.01666) == BTTask::FAILURE);
|
||||
}
|
||||
|
||||
} //namespace TestRandomSelector
|
||||
|
||||
#endif // TEST_RANDOM_SELECTOR_H
|
|
@ -0,0 +1,110 @@
|
|||
/**
|
||||
* test_random_sequence.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_RANDOM_SEQUENCE_H
|
||||
#define TEST_RANDOM_SEQUENCE_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/composites/bt_random_sequence.h"
|
||||
|
||||
namespace TestRandomSequence {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTRandomSequence") {
|
||||
Ref<BTRandomSequence> seq = memnew(BTRandomSequence);
|
||||
Ref<BTTestAction> task1 = memnew(BTTestAction());
|
||||
Ref<BTTestAction> task2 = memnew(BTTestAction());
|
||||
Ref<BTTestAction> task3 = memnew(BTTestAction());
|
||||
|
||||
seq->add_child(task1);
|
||||
seq->add_child(task2);
|
||||
seq->add_child(task3);
|
||||
|
||||
REQUIRE(seq->get_child_count() == 3);
|
||||
|
||||
SUBCASE("Expecting RUNNING status when a child task returns RUNNING") {
|
||||
task1->ret_status = BTTask::SUCCESS;
|
||||
task2->ret_status = BTTask::RUNNING;
|
||||
task3->ret_status = BTTask::SUCCESS;
|
||||
|
||||
CHECK(seq->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->is_status_either(BTTask::SUCCESS, BTTask::FRESH));
|
||||
CHECK(task2->get_status() == BTTask::RUNNING);
|
||||
CHECK(task3->is_status_either(BTTask::SUCCESS, BTTask::FRESH));
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS_UP_TO(task1, 1, 1, 1); // * ran no more than once
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 0); // * running - enters and ticks
|
||||
CHECK_ENTRIES_TICKS_EXITS_UP_TO(task3, 1, 1, 1); // * ran no more than once
|
||||
|
||||
SUBCASE("Resuming and succeeding when all tasks succeed") {
|
||||
task2->ret_status = BTTask::SUCCESS;
|
||||
|
||||
CHECK(seq->execute(0.01666) == BTTask::SUCCESS);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task3->get_status() == BTTask::SUCCESS);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1); // * ran once
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 2, 1); // * finishes - ticks and exits with SUCCESS
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 1, 1); // * ran once
|
||||
}
|
||||
|
||||
SUBCASE("Resuming and failing when a child task fails") {
|
||||
task2->ret_status = BTTask::FAILURE;
|
||||
|
||||
CHECK(seq->execute(0.01666) == BTTask::FAILURE);
|
||||
|
||||
CHECK(task1->is_status_either(BTTask::SUCCESS, BTTask::FRESH));
|
||||
CHECK(task2->get_status() == BTTask::FAILURE);
|
||||
CHECK(task3->is_status_either(BTTask::SUCCESS, BTTask::FRESH));
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS_UP_TO(task1, 1, 1, 1); // * ran no more than once
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 2, 1); // * finishes - ticks and exits with FAILURE
|
||||
CHECK_ENTRIES_TICKS_EXITS_UP_TO(task3, 1, 1, 1); // * ran no more than once
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Verify that tasks are executed in random order") {
|
||||
task1->ret_status = BTTask::SUCCESS;
|
||||
task2->ret_status = BTTask::SUCCESS;
|
||||
task3->ret_status = BTTask::RUNNING;
|
||||
|
||||
int num_tries = 10;
|
||||
bool is_confirmed = false;
|
||||
while (!is_confirmed && num_tries--) {
|
||||
CHECK(seq->execute(0.01666) == BTTask::RUNNING);
|
||||
int checksum = 0;
|
||||
if (task1->get_status() == BTTask::SUCCESS) {
|
||||
checksum += 1;
|
||||
}
|
||||
if (task2->get_status() == BTTask::SUCCESS) {
|
||||
checksum += 2;
|
||||
}
|
||||
if (task3->get_status() == BTTask::RUNNING) {
|
||||
checksum += 4;
|
||||
}
|
||||
is_confirmed = (checksum != 7);
|
||||
}
|
||||
CHECK(is_confirmed);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] Empty BTRandomSequence returns SUCCESS") {
|
||||
Ref<BTRandomSequence> seq = memnew(BTRandomSequence);
|
||||
CHECK(seq->execute(0.01666) == BTTask::SUCCESS);
|
||||
}
|
||||
|
||||
} //namespace TestRandomSequence
|
||||
|
||||
#endif // TEST_RANDOM_SEQUENCE_H
|
|
@ -0,0 +1,105 @@
|
|||
/**
|
||||
* test_repeat.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_REPEAT_H
|
||||
#define TEST_REPEAT_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/decorators/bt_repeat.h"
|
||||
|
||||
namespace TestRepeat {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTRepeat") {
|
||||
Ref<BTRepeat> rep = memnew(BTRepeat);
|
||||
|
||||
SUBCASE("When empty") {
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(rep->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
Ref<BTTestAction> task = memnew(BTTestAction);
|
||||
rep->add_child(task);
|
||||
|
||||
SUBCASE("When repeating forever") {
|
||||
rep->set_times(3);
|
||||
rep->set_forever(true);
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
|
||||
for (int i = 1; i <= 100; i++) {
|
||||
CHECK(rep->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, i, i, i);
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("When repeated x3 times") {
|
||||
rep->set_times(3);
|
||||
rep->set_forever(false);
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
|
||||
CHECK(rep->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 1, 1, 1);
|
||||
CHECK(rep->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 2, 2, 2);
|
||||
CHECK(rep->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 3, 3, 3);
|
||||
}
|
||||
|
||||
SUBCASE("When the child task takes more than one tick to finish") {
|
||||
rep->set_times(2);
|
||||
rep->set_forever(false);
|
||||
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
CHECK(rep->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::RUNNING, 1, 1, 0);
|
||||
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(rep->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 1, 2, 1);
|
||||
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
CHECK(rep->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::RUNNING, 2, 3, 1);
|
||||
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(rep->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 2, 4, 2);
|
||||
}
|
||||
|
||||
SUBCASE("When the child task fails") {
|
||||
rep->set_times(2);
|
||||
rep->set_forever(false);
|
||||
|
||||
task->ret_status = BTTask::FAILURE;
|
||||
|
||||
SUBCASE("When set to abort on failure") {
|
||||
rep->set_abort_on_failure(true);
|
||||
CHECK(rep->execute(0.01666) == BTTask::FAILURE);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::FAILURE, 1, 1, 1);
|
||||
CHECK(rep->execute(0.01666) == BTTask::FAILURE);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::FAILURE, 2, 2, 2);
|
||||
}
|
||||
|
||||
SUBCASE("When not set to abort on failure") {
|
||||
rep->set_abort_on_failure(false);
|
||||
CHECK(rep->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::FAILURE, 1, 1, 1);
|
||||
CHECK(rep->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::FAILURE, 2, 2, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestRepeat
|
||||
|
||||
#endif // TEST_REPEAT_H
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* test_repeat_until_failure.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_REPEAT_UNTIL_FAILURE_H
|
||||
#define TEST_REPEAT_UNTIL_FAILURE_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/decorators/bt_repeat_until_failure.h"
|
||||
|
||||
namespace TestRepeatUntilFailure {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTRepeatUntilFailure") {
|
||||
Ref<BTRepeatUntilFailure> rep = memnew(BTRepeatUntilFailure);
|
||||
|
||||
SUBCASE("When empty") {
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(rep->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
Ref<BTTestAction> task = memnew(BTTestAction);
|
||||
rep->add_child(task);
|
||||
|
||||
SUBCASE("With various return statuses") {
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(rep->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 1, 1, 1);
|
||||
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
CHECK(rep->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::RUNNING, 2, 2, 1);
|
||||
|
||||
task->ret_status = BTTask::FAILURE;
|
||||
CHECK(rep->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::FAILURE, 2, 3, 2);
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestRepeatUntilFailure
|
||||
|
||||
#endif // TEST_REPEAT_UNTIL_FAILURE_H
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* test_repeat_until_success.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_REPEAT_UNTIL_SUCCESS_H
|
||||
#define TEST_REPEAT_UNTIL_SUCCESS_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/decorators/bt_repeat_until_success.h"
|
||||
|
||||
namespace TestRepeatUntilSuccess {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTRepeatUntilSuccess") {
|
||||
Ref<BTRepeatUntilSuccess> rep = memnew(BTRepeatUntilSuccess);
|
||||
|
||||
SUBCASE("When empty") {
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(rep->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
Ref<BTTestAction> task = memnew(BTTestAction);
|
||||
rep->add_child(task);
|
||||
|
||||
SUBCASE("With various return statuses") {
|
||||
task->ret_status = BTTask::FAILURE;
|
||||
CHECK(rep->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::FAILURE, 1, 1, 1);
|
||||
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
CHECK(rep->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::RUNNING, 2, 2, 1);
|
||||
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(rep->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 2, 3, 2);
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestRepeatUntilSuccess
|
||||
|
||||
#endif // TEST_REPEAT_UNTIL_SUCCESS_H
|
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
* test_run_limit.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_RUN_LIMIT_H
|
||||
#define TEST_RUN_LIMIT_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/decorators/bt_run_limit.h"
|
||||
|
||||
namespace TestRunLimit {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTRunLimit") {
|
||||
Ref<BTRunLimit> lim = memnew(BTRunLimit);
|
||||
|
||||
SUBCASE("When empty") {
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(lim->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
Ref<BTTestAction> task = memnew(BTTestAction);
|
||||
lim->add_child(task);
|
||||
|
||||
SUBCASE("With run limit set to 2") {
|
||||
lim->set_run_limit(2);
|
||||
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(lim->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 1, 1, 1); // * task executed
|
||||
|
||||
SUBCASE("When the child task succeeds") {
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(lim->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 2, 2, 2); // * task executed
|
||||
}
|
||||
SUBCASE("When the child task fails") {
|
||||
task->ret_status = BTTask::FAILURE;
|
||||
CHECK(lim->execute(0.01666) == BTTask::FAILURE);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::FAILURE, 2, 2, 2); // * task executed
|
||||
}
|
||||
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
|
||||
CHECK(lim->execute(0.01666) == BTTask::FAILURE);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 2, 2, 2); // * task not executed
|
||||
|
||||
CHECK(lim->execute(0.01666) == BTTask::FAILURE);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 2, 2, 2); // * task not executed
|
||||
}
|
||||
|
||||
SUBCASE("When the child task takes more than one tick to finish") {
|
||||
lim->set_run_limit(2);
|
||||
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
CHECK(lim->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::RUNNING, 1, 1, 0);
|
||||
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(lim->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 1, 2, 1);
|
||||
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
CHECK(lim->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::RUNNING, 2, 3, 1);
|
||||
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(lim->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 2, 4, 2);
|
||||
|
||||
CHECK(lim->execute(0.01666) == BTTask::FAILURE);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task, 2, 4, 2);
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestRunLimit
|
||||
|
||||
#endif // TEST_RUN_LIMIT_H
|
|
@ -0,0 +1,141 @@
|
|||
/**
|
||||
* test_selector.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_SELECTOR_H
|
||||
#define TEST_SELECTOR_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/composites/bt_selector.h"
|
||||
|
||||
namespace TestSelector {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTSelector when all return FAILURE") {
|
||||
Ref<BTSelector> sel = memnew(BTSelector);
|
||||
Ref<BTTestAction> task1 = memnew(BTTestAction(BTTask::FAILURE));
|
||||
Ref<BTTestAction> task2 = memnew(BTTestAction(BTTask::FAILURE));
|
||||
Ref<BTTestAction> task3 = memnew(BTTestAction(BTTask::FAILURE));
|
||||
|
||||
sel->add_child(task1);
|
||||
sel->add_child(task2);
|
||||
sel->add_child(task3);
|
||||
|
||||
REQUIRE(sel->get_child_count() == 3);
|
||||
|
||||
// * First execution.
|
||||
CHECK(sel->execute(0.01666) == BTTask::FAILURE);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::FAILURE);
|
||||
CHECK(task2->get_status() == BTTask::FAILURE);
|
||||
CHECK(task3->get_status() == BTTask::FAILURE);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 1, 1);
|
||||
|
||||
// * Second execution.
|
||||
CHECK(sel->execute(0.01666) == BTTask::FAILURE);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::FAILURE);
|
||||
CHECK(task2->get_status() == BTTask::FAILURE);
|
||||
CHECK(task3->get_status() == BTTask::FAILURE);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 2, 2, 2);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 2, 2, 2);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 2, 2, 2);
|
||||
}
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTSelector when second returns SUCCESS") {
|
||||
Ref<BTSelector> sel = memnew(BTSelector);
|
||||
Ref<BTTestAction> task1 = memnew(BTTestAction(BTTask::FAILURE));
|
||||
Ref<BTTestAction> task2 = memnew(BTTestAction(BTTask::SUCCESS));
|
||||
Ref<BTTestAction> task3 = memnew(BTTestAction(BTTask::FAILURE));
|
||||
|
||||
sel->add_child(task1);
|
||||
sel->add_child(task2);
|
||||
sel->add_child(task3);
|
||||
|
||||
REQUIRE(sel->get_child_count() == 3);
|
||||
|
||||
// * First execution.
|
||||
CHECK(sel->execute(0.01666) == BTTask::SUCCESS);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::FAILURE);
|
||||
CHECK(task2->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task3->get_status() == BTTask::FRESH);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 0, 0, 0);
|
||||
|
||||
// * Second execution.
|
||||
CHECK(sel->execute(0.01666) == BTTask::SUCCESS);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::FAILURE);
|
||||
CHECK(task2->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task3->get_status() == BTTask::FRESH);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 2, 2, 2);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 2, 2, 2);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 0, 0, 0);
|
||||
}
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTSelector when second returns RUNNING") {
|
||||
Ref<BTSelector> sel = memnew(BTSelector);
|
||||
Ref<BTTestAction> task1 = memnew(BTTestAction(BTTask::FAILURE));
|
||||
Ref<BTTestAction> task2 = memnew(BTTestAction(BTTask::RUNNING));
|
||||
Ref<BTTestAction> task3 = memnew(BTTestAction(BTTask::FAILURE));
|
||||
|
||||
sel->add_child(task1);
|
||||
sel->add_child(task2);
|
||||
sel->add_child(task3);
|
||||
|
||||
REQUIRE(sel->get_child_count() == 3);
|
||||
|
||||
// * First execution.
|
||||
CHECK(sel->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::FAILURE);
|
||||
CHECK(task2->get_status() == BTTask::RUNNING);
|
||||
CHECK(task3->get_status() == BTTask::FRESH);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 0);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 0, 0, 0);
|
||||
|
||||
// * Second execution.
|
||||
CHECK(sel->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::FAILURE);
|
||||
CHECK(task2->get_status() == BTTask::RUNNING);
|
||||
CHECK(task3->get_status() == BTTask::FRESH);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 2, 0);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 0, 0, 0);
|
||||
|
||||
// * Third execution with second task returning FAILURE.
|
||||
task2->ret_status = BTTask::FAILURE;
|
||||
CHECK(sel->execute(0.01666) == BTTask::FAILURE);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::FAILURE);
|
||||
CHECK(task2->get_status() == BTTask::FAILURE);
|
||||
CHECK(task3->get_status() == BTTask::FAILURE);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 3, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 1, 1);
|
||||
}
|
||||
|
||||
} //namespace TestSelector
|
||||
|
||||
#endif // TEST_SELECTOR_H
|
|
@ -0,0 +1,144 @@
|
|||
/**
|
||||
* test_sequence.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_SEQUENCE_H
|
||||
#define TEST_SEQUENCE_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/composites/bt_sequence.h"
|
||||
|
||||
namespace TestSequence {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTSequence when all return SUCCESS") {
|
||||
Ref<BTSequence> seq = memnew(BTSequence);
|
||||
Ref<BTTestAction> task1 = memnew(BTTestAction(BTTask::SUCCESS));
|
||||
Ref<BTTestAction> task2 = memnew(BTTestAction(BTTask::SUCCESS));
|
||||
Ref<BTTestAction> task3 = memnew(BTTestAction(BTTask::SUCCESS));
|
||||
|
||||
seq->add_child(task1);
|
||||
seq->add_child(task2);
|
||||
seq->add_child(task3);
|
||||
|
||||
REQUIRE(seq->get_child_count() == 3);
|
||||
|
||||
// * First execution.
|
||||
CHECK(seq->execute(0.01666) == BTTask::SUCCESS);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task3->get_status() == BTTask::SUCCESS);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 1, 1);
|
||||
|
||||
// * Second execution.
|
||||
CHECK(seq->execute(0.01666) == BTTask::SUCCESS);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task3->get_status() == BTTask::SUCCESS);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 2, 2, 2);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 2, 2, 2);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 2, 2, 2);
|
||||
}
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTSequence when second returns FAILURE") {
|
||||
Ref<BTSequence> seq = memnew(BTSequence);
|
||||
Ref<BTTestAction> task1 = memnew(BTTestAction(BTTask::SUCCESS));
|
||||
Ref<BTTestAction> task2 = memnew(BTTestAction(BTTask::FAILURE));
|
||||
Ref<BTTestAction> task3 = memnew(BTTestAction(BTTask::SUCCESS));
|
||||
|
||||
seq->add_child(task1);
|
||||
seq->add_child(task2);
|
||||
seq->add_child(task3);
|
||||
|
||||
REQUIRE(seq->get_child_count() == 3);
|
||||
|
||||
// * First execution.
|
||||
CHECK(seq->execute(0.01666) == BTTask::FAILURE);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::FAILURE);
|
||||
CHECK(task3->get_status() == BTTask::FRESH);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 0, 0, 0);
|
||||
|
||||
// * Second execution.
|
||||
CHECK(seq->execute(0.01666) == BTTask::FAILURE);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::FAILURE);
|
||||
CHECK(task3->get_status() == BTTask::FRESH);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 2, 2, 2);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 2, 2, 2);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 0, 0, 0);
|
||||
}
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTSequence when second returns RUNNING") {
|
||||
Ref<BTSequence> seq = memnew(BTSequence);
|
||||
Ref<BTTestAction> task1 = memnew(BTTestAction(BTTask::SUCCESS));
|
||||
Ref<BTTestAction> task2 = memnew(BTTestAction(BTTask::RUNNING));
|
||||
Ref<BTTestAction> task3 = memnew(BTTestAction(BTTask::SUCCESS));
|
||||
|
||||
seq->add_child(task1);
|
||||
seq->add_child(task2);
|
||||
seq->add_child(task3);
|
||||
|
||||
REQUIRE(seq->get_child_count() == 3);
|
||||
|
||||
// * First execution.
|
||||
CHECK(seq->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::RUNNING);
|
||||
CHECK(task3->get_status() == BTTask::FRESH);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 1, 0);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 0, 0, 0);
|
||||
|
||||
// * Second execution.
|
||||
CHECK(seq->execute(0.01666) == BTTask::RUNNING);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 2, 0);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 0, 0, 0);
|
||||
|
||||
// * Third execution with second task returning SUCCESS.
|
||||
task2->ret_status = BTTask::SUCCESS;
|
||||
CHECK(seq->execute(0.01666) == BTTask::SUCCESS);
|
||||
|
||||
CHECK(task1->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task2->get_status() == BTTask::SUCCESS);
|
||||
CHECK(task3->get_status() == BTTask::SUCCESS);
|
||||
|
||||
CHECK_ENTRIES_TICKS_EXITS(task1, 1, 1, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task2, 1, 3, 1);
|
||||
CHECK_ENTRIES_TICKS_EXITS(task3, 1, 1, 1);
|
||||
}
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTSequence with no child tasks") {
|
||||
Ref<BTSequence> seq = memnew(BTSequence);
|
||||
|
||||
REQUIRE(seq->get_child_count() == 0);
|
||||
CHECK(seq->execute(0.01666) == BTTask::SUCCESS);
|
||||
}
|
||||
|
||||
} //namespace TestSequence
|
||||
|
||||
#endif // TEST_SEQUENCE_H
|
|
@ -0,0 +1,104 @@
|
|||
/**
|
||||
* test_set_agent_property.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_SET_AGENT_PROPERTY_H
|
||||
#define TEST_SET_AGENT_PROPERTY_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/blackboard/bb_param/bb_param.h"
|
||||
#include "modules/limboai/blackboard/bb_param/bb_variant.h"
|
||||
#include "modules/limboai/blackboard/blackboard.h"
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/scene/bt_set_agent_property.h"
|
||||
|
||||
#include "core/os/memory.h"
|
||||
|
||||
namespace TestSetAgentProperty {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTSetAgentProperty") {
|
||||
Ref<BTSetAgentProperty> sap = memnew(BTSetAgentProperty);
|
||||
Node *agent = memnew(Node);
|
||||
Ref<Blackboard> bb = memnew(Blackboard);
|
||||
sap->initialize(agent, bb);
|
||||
|
||||
sap->set_property("process_priority"); // * property that will be set by the task
|
||||
Ref<BBVariant> value_param = memnew(BBVariant);
|
||||
value_param->set_value_source(BBParam::SAVED_VALUE);
|
||||
value_param->set_saved_value(7);
|
||||
sap->set_value(value_param);
|
||||
|
||||
SUBCASE("With integer") {
|
||||
CHECK(sap->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK(agent->get_process_priority() == 7);
|
||||
}
|
||||
SUBCASE("When value is not set") {
|
||||
sap->set_value(nullptr);
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(sap->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("When property is empty") {
|
||||
sap->set_property("");
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(sap->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("When property doesn't exist") {
|
||||
sap->set_property("not_found");
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(sap->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("With StringName and String") {
|
||||
value_param->set_saved_value("TestName");
|
||||
sap->set_property("name");
|
||||
CHECK(sap->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK(agent->get_name() == "TestName");
|
||||
}
|
||||
SUBCASE("With blackboard variable") {
|
||||
value_param->set_value_source(BBParam::BLACKBOARD_VAR);
|
||||
value_param->set_variable("priority");
|
||||
|
||||
SUBCASE("With proper BB variable") {
|
||||
bb->set_var("priority", 8);
|
||||
CHECK(sap->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK(agent->get_process_priority() == 8);
|
||||
}
|
||||
SUBCASE("With BB variable of wrong type") {
|
||||
bb->set_var("priority", "high");
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(sap->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
CHECK(agent->get_process_priority() == 0);
|
||||
}
|
||||
SUBCASE("When BB variable doesn't exist") {
|
||||
value_param->set_variable("not_found");
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(sap->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
CHECK(agent->get_process_priority() == 0);
|
||||
}
|
||||
SUBCASE("When BB variable isn't set") {
|
||||
value_param->set_variable("");
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(sap->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
CHECK(agent->get_process_priority() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
memdelete(agent);
|
||||
}
|
||||
|
||||
} //namespace TestSetAgentProperty
|
||||
|
||||
#endif // TEST_SET_AGENT_PROPERTY_H
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* test_set_var.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_SET_VAR_H
|
||||
#define TEST_SET_VAR_H
|
||||
|
||||
#include "core/variant/variant.h"
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/blackboard/bb_param/bb_param.h"
|
||||
#include "modules/limboai/blackboard/bb_param/bb_variant.h"
|
||||
#include "modules/limboai/blackboard/blackboard.h"
|
||||
#include "modules/limboai/bt/tasks/blackboard/bt_set_var.h"
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestSetVar {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTSetVar") {
|
||||
Ref<BTSetVar> sv = memnew(BTSetVar);
|
||||
Ref<Blackboard> bb = memnew(Blackboard);
|
||||
Node *dummy = memnew(Node);
|
||||
|
||||
sv->initialize(dummy, bb);
|
||||
|
||||
SUBCASE("When variable is not set") {
|
||||
ERR_PRINT_OFF;
|
||||
sv->set_variable("");
|
||||
CHECK(sv->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
SUBCASE("With variable set") {
|
||||
Ref<BBVariant> value = memnew(BBVariant);
|
||||
sv->set_value(value);
|
||||
sv->set_variable("var");
|
||||
|
||||
SUBCASE("When setting to a provided value") {
|
||||
value->set_value_source(BBParam::SAVED_VALUE);
|
||||
value->set_saved_value(123);
|
||||
CHECK(sv->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK(bb->get_var("var", 0) == Variant(123));
|
||||
}
|
||||
SUBCASE("When assigning value of another blackboard variable") {
|
||||
value->set_value_source(BBParam::BLACKBOARD_VAR);
|
||||
|
||||
SUBCASE("BB variable is empty") {
|
||||
ERR_PRINT_OFF;
|
||||
value->set_variable("");
|
||||
CHECK(sv->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("BB variable doesn't exist") {
|
||||
ERR_PRINT_OFF;
|
||||
Variant initial_value = Variant(777);
|
||||
bb->set_var("var", initial_value);
|
||||
value->set_variable("not_found");
|
||||
CHECK(sv->execute(0.01666) == BTTask::FAILURE);
|
||||
CHECK(bb->get_var("var", 0) == initial_value); // * Check initial value is intact.
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("BB variable exists") {
|
||||
value->set_variable("compare_var");
|
||||
bb->set_var("compare_var", 123);
|
||||
CHECK(sv->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK(bb->get_var("var", 0) == Variant(123));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestSetVar
|
||||
|
||||
#endif // TEST_SET_VAR_H
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* test_stop_animation.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_STOP_ANIMATION_H
|
||||
#define TEST_STOP_ANIMATION_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/scene/bt_stop_animation.h"
|
||||
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/resources/animation.h"
|
||||
#include "scene/resources/animation_library.h"
|
||||
|
||||
namespace TestStopAnimation {
|
||||
|
||||
TEST_CASE("[SceneTree][LimboAI] BTStopAnimation") {
|
||||
AnimationPlayer *player = memnew(AnimationPlayer);
|
||||
SceneTree::get_singleton()->get_root()->add_child(player);
|
||||
player->set_process_callback(AnimationPlayer::AnimationProcessCallback::ANIMATION_PROCESS_IDLE);
|
||||
|
||||
Ref<AnimationLibrary> anim_lib = memnew(AnimationLibrary);
|
||||
Ref<Animation> anim = memnew(Animation);
|
||||
anim->set_name("test");
|
||||
anim->set_length(0.1);
|
||||
anim->set_loop_mode(Animation::LOOP_NONE);
|
||||
REQUIRE(anim_lib->add_animation("test", anim) == OK);
|
||||
REQUIRE(player->add_animation_library("", anim_lib) == OK);
|
||||
REQUIRE(player->has_animation("test"));
|
||||
|
||||
Ref<BTStopAnimation> sa = memnew(BTStopAnimation);
|
||||
Ref<BBNode> player_param = memnew(BBNode);
|
||||
sa->set_animation_player(player_param);
|
||||
Node *dummy = memnew(Node);
|
||||
SceneTree::get_singleton()->get_root()->add_child(dummy);
|
||||
Ref<Blackboard> bb = memnew(Blackboard);
|
||||
|
||||
SUBCASE("When AnimationPlayer doesn't exist") {
|
||||
player_param->set_saved_value(NodePath("./NotFound"));
|
||||
ERR_PRINT_OFF;
|
||||
sa->initialize(dummy, bb);
|
||||
CHECK(sa->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("When AnimationPlayer exists") {
|
||||
player_param->set_saved_value(player->get_path());
|
||||
sa->initialize(dummy, bb);
|
||||
|
||||
SUBCASE("When AnimationPlayer is not playing") {
|
||||
REQUIRE_FALSE(player->is_playing());
|
||||
CHECK(sa->execute(0.01666) == BTTask::SUCCESS);
|
||||
}
|
||||
SUBCASE("When AnimationPlayer is playing") {
|
||||
player->play("test");
|
||||
REQUIRE(player->is_playing());
|
||||
CHECK(sa->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK_FALSE(player->is_playing());
|
||||
}
|
||||
}
|
||||
|
||||
memdelete(dummy);
|
||||
memdelete(player);
|
||||
}
|
||||
|
||||
} //namespace TestStopAnimation
|
||||
|
||||
#endif // TEST_STOP_ANIMATION_H
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* test_subtree.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_SUBTREE_H
|
||||
#define TEST_SUBTREE_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/behavior_tree.h"
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/decorators/bt_subtree.h"
|
||||
|
||||
namespace TestSubtree {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTSubtree") {
|
||||
ClassDB::register_class<BTTestAction>();
|
||||
|
||||
Ref<BTSubtree> st = memnew(BTSubtree);
|
||||
Ref<Blackboard> bb = memnew(Blackboard);
|
||||
Node *dummy = memnew(Node);
|
||||
|
||||
SUBCASE("When empty") {
|
||||
ERR_PRINT_OFF;
|
||||
st->initialize(dummy, bb);
|
||||
CHECK(st->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
SUBCASE("With a subtree assigned") {
|
||||
Ref<BehaviorTree> bt = memnew(BehaviorTree);
|
||||
Ref<BTTestAction> task = memnew(BTTestAction(BTTask::SUCCESS));
|
||||
bt->set_root_task(task);
|
||||
st->set_subtree(bt);
|
||||
|
||||
CHECK(st->get_child_count() == 0);
|
||||
st->initialize(dummy, bb);
|
||||
CHECK(st->get_child_count() == 1);
|
||||
CHECK(st->get_child(0) != task);
|
||||
|
||||
Ref<BTTestAction> ta = st->get_child(0);
|
||||
REQUIRE(ta.is_valid());
|
||||
|
||||
SUBCASE("When child succeeds") {
|
||||
ta->ret_status = BTTask::SUCCESS;
|
||||
CHECK(st->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(ta, BTTask::SUCCESS, 1, 1, 1);
|
||||
}
|
||||
SUBCASE("When child fails") {
|
||||
ta->ret_status = BTTask::FAILURE;
|
||||
CHECK(st->execute(0.01666) == BTTask::FAILURE);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(ta, BTTask::FAILURE, 1, 1, 1);
|
||||
}
|
||||
SUBCASE("When child is running") {
|
||||
ta->ret_status = BTTask::RUNNING;
|
||||
CHECK(st->execute(0.01666) == BTTask::RUNNING);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(ta, BTTask::RUNNING, 1, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
memdelete(dummy);
|
||||
}
|
||||
|
||||
} //namespace TestSubtree
|
||||
|
||||
#endif // TEST_SUBTREE_H
|
|
@ -0,0 +1,215 @@
|
|||
/**
|
||||
* test_task.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_TASK_H
|
||||
#define TEST_TASK_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/blackboard/blackboard.h"
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestTask {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTTask") {
|
||||
SUBCASE("Test with hierarchy") {
|
||||
Ref<BTTask> task = memnew(BTTask);
|
||||
Ref<BTTask> child1 = memnew(BTTask);
|
||||
Ref<BTTask> child3 = memnew(BTTask);
|
||||
|
||||
// * add_child, get_child_count & get_child
|
||||
REQUIRE(task->get_child_count() == 0);
|
||||
task->add_child(child1);
|
||||
task->add_child(child3);
|
||||
REQUIRE(task->get_child_count() == 2);
|
||||
REQUIRE(task->get_child(0) == child1);
|
||||
REQUIRE(task->get_child(1) == child3);
|
||||
|
||||
// * add_child_at_index
|
||||
Ref<BTTask> child2 = memnew(BTTask);
|
||||
task->add_child_at_index(child2, 1);
|
||||
REQUIRE(task->get_child_count() == 3);
|
||||
REQUIRE(task->get_child(0) == child1);
|
||||
REQUIRE(task->get_child(1) == child2);
|
||||
REQUIRE(task->get_child(2) == child3);
|
||||
|
||||
/** Hierarchy:
|
||||
* task->
|
||||
* -> child1 (0)
|
||||
* -> child2 (1)
|
||||
* -> child3 (2)
|
||||
*/
|
||||
|
||||
SUBCASE("Test has_child()") {
|
||||
CHECK(task->has_child(child1));
|
||||
CHECK(task->has_child(child2));
|
||||
CHECK(task->has_child(child3));
|
||||
|
||||
Ref<BTTask> other = memnew(BTTask);
|
||||
CHECK_FALSE(task->has_child(other));
|
||||
}
|
||||
SUBCASE("Test get_child_index()") {
|
||||
CHECK(task->get_child_index(child1) == 0);
|
||||
CHECK(task->get_child_index(child2) == 1);
|
||||
CHECK(task->get_child_index(child3) == 2);
|
||||
}
|
||||
SUBCASE("Test get_child_index() with an out-of-hierarchy task") {
|
||||
Ref<BTTask> other = memnew(BTTask);
|
||||
CHECK(task->get_child_index(other) == -1);
|
||||
}
|
||||
SUBCASE("Test is_descendant_of()") {
|
||||
Ref<BTTask> grandchild = memnew(BTTask);
|
||||
child1->add_child(grandchild);
|
||||
CHECK(child1->has_child(grandchild));
|
||||
CHECK(child1->get_child_count() == 1);
|
||||
CHECK(grandchild->is_descendant_of(task));
|
||||
}
|
||||
SUBCASE("Test next_sibling()") {
|
||||
CHECK(child1->next_sibling() == child2);
|
||||
CHECK(child2->next_sibling() == child3);
|
||||
CHECK(child3->next_sibling() == nullptr);
|
||||
}
|
||||
SUBCASE("Test remove_child()") {
|
||||
task->remove_child(child2);
|
||||
REQUIRE(task->get_child_count() == 2);
|
||||
CHECK(task->get_child(0) == child1);
|
||||
CHECK(task->get_child(1) == child3);
|
||||
|
||||
task->remove_child(child3);
|
||||
REQUIRE(task->get_child_count() == 1);
|
||||
CHECK(task->get_child(0) == child1);
|
||||
|
||||
task->remove_child(child1);
|
||||
REQUIRE(task->get_child_count() == 0);
|
||||
}
|
||||
SUBCASE("Test remove_child() with an out-of-hierarchy task") {
|
||||
Ref<BTTask> other = memnew(BTTask);
|
||||
// * Must not crash.
|
||||
ERR_PRINT_OFF;
|
||||
task->remove_child(other);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("Test remove_at_index()") {
|
||||
task->remove_child_at_index(1);
|
||||
REQUIRE(task->get_child_count() == 2);
|
||||
CHECK(task->get_child(0) == child1);
|
||||
CHECK(task->get_child(1) == child3);
|
||||
|
||||
task->remove_child_at_index(1);
|
||||
REQUIRE(task->get_child_count() == 1);
|
||||
CHECK(task->get_child(0) == child1);
|
||||
|
||||
task->remove_child_at_index(0);
|
||||
REQUIRE(task->get_child_count() == 0);
|
||||
}
|
||||
SUBCASE("Test remove_child_at_index() with an out-of-bounds index") {
|
||||
// * Must not crash.
|
||||
ERR_PRINT_OFF;
|
||||
task->remove_child_at_index(-1);
|
||||
CHECK(task->get_child_count() == 3);
|
||||
task->remove_child_at_index(task->get_child_count());
|
||||
CHECK(task->get_child_count() == 3);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("Test is_root()") {
|
||||
CHECK(task->is_root());
|
||||
CHECK_FALSE(child1->is_root());
|
||||
CHECK_FALSE(child2->is_root());
|
||||
CHECK_FALSE(child3->is_root());
|
||||
}
|
||||
SUBCASE("Test get_root()") {
|
||||
CHECK(task->get_root() == task);
|
||||
CHECK(child1->get_root() == task);
|
||||
CHECK(child2->get_root() == task);
|
||||
CHECK(child3->get_root() == task);
|
||||
}
|
||||
SUBCASE("Test get_parent()") {
|
||||
CHECK(task->get_parent() == nullptr);
|
||||
CHECK(child1->get_parent() == task);
|
||||
CHECK(child2->get_parent() == task);
|
||||
CHECK(child2->get_parent() == task);
|
||||
}
|
||||
SUBCASE("Test initialize()") {
|
||||
Node *dummy = memnew(Node);
|
||||
Ref<Blackboard> bb = memnew(Blackboard);
|
||||
SUBCASE("With valid parameters") {
|
||||
task->initialize(dummy, bb);
|
||||
CHECK(task->get_agent() == dummy);
|
||||
CHECK(task->get_blackboard() == bb);
|
||||
CHECK(child1->get_agent() == dummy);
|
||||
CHECK(child1->get_blackboard() == bb);
|
||||
CHECK(child2->get_agent() == dummy);
|
||||
CHECK(child2->get_blackboard() == bb);
|
||||
CHECK(child3->get_agent() == dummy);
|
||||
CHECK(child3->get_blackboard() == bb);
|
||||
}
|
||||
SUBCASE("Test if not crashes when agent is null") {
|
||||
ERR_PRINT_OFF;
|
||||
task->initialize(nullptr, bb);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
SUBCASE("Test if not crashes when BB is null") {
|
||||
ERR_PRINT_OFF;
|
||||
task->initialize(dummy, nullptr);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
memdelete(dummy);
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Test get_elapsed_time()") {
|
||||
Ref<BTTestAction> task = memnew(BTTestAction);
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
|
||||
CHECK(task->get_elapsed_time() == 0.0);
|
||||
|
||||
task->execute(888.0);
|
||||
CHECK(task->get_elapsed_time() == 0.0); // * delta_time shouldn't contribute to the elapsed_time on the first tick.
|
||||
task->execute(10.0);
|
||||
CHECK(task->get_elapsed_time() == 10.0);
|
||||
task->execute(10.0);
|
||||
CHECK(task->get_elapsed_time() == 20.0);
|
||||
|
||||
SUBCASE("When finishing with SUCCESS or FAILURE") {
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
task->execute(10.0);
|
||||
CHECK(task->get_elapsed_time() == 0.0);
|
||||
}
|
||||
SUBCASE("When cancelled") {
|
||||
task->cancel();
|
||||
CHECK(task->get_elapsed_time() == 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Test clone()") {
|
||||
// * Note: BTTask cannot be duplicated, thus using BTTestAction.
|
||||
Ref<BTTestAction> task = memnew(BTTestAction);
|
||||
Ref<BTTestAction> child1 = memnew(BTTestAction);
|
||||
Ref<BTTestAction> child2 = memnew(BTTestAction);
|
||||
|
||||
task->add_child(child1);
|
||||
task->add_child(child2);
|
||||
REQUIRE(task->get_child_count() == 2);
|
||||
REQUIRE(task->get_child(0) == child1);
|
||||
REQUIRE(task->get_child(1) == child2);
|
||||
|
||||
Ref<BTTestAction> cloned = task->clone();
|
||||
CHECK_FALSE(cloned == task);
|
||||
REQUIRE(cloned->get_child_count() == 2);
|
||||
CHECK_FALSE(cloned->get_child(0) == child1);
|
||||
CHECK_FALSE(cloned->get_child(1) == child2);
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestTask
|
||||
|
||||
#endif // TEST_TASK_H
|
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* test_time_limit.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_TIME_LIMIT_H
|
||||
#define TEST_TIME_LIMIT_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/decorators/bt_time_limit.h"
|
||||
|
||||
namespace TestTimeLimit {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTTimeLimit") {
|
||||
Ref<BTTimeLimit> lim = memnew(BTTimeLimit);
|
||||
|
||||
SUBCASE("When empty") {
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(lim->execute(0.01666) == BTTask::FAILURE);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
Ref<BTTestAction> task = memnew(BTTestAction);
|
||||
lim->add_child(task);
|
||||
lim->set_time_limit(1.0);
|
||||
|
||||
SUBCASE("With a long-running task") {
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
|
||||
CHECK(lim->execute(0.0) == BTTask::RUNNING); // * elapsed 0.0
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::RUNNING, 1, 1, 0); // * running
|
||||
|
||||
CHECK(lim->execute(0.4) == BTTask::RUNNING); // * elapsed 0.4
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::RUNNING, 1, 2, 0); // * running
|
||||
|
||||
CHECK(lim->execute(0.4) == BTTask::RUNNING); // * elapsed 0.8
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::RUNNING, 1, 3, 0); // * running
|
||||
|
||||
SUBCASE("When exceeding the time limit") {
|
||||
CHECK(lim->execute(0.4) == BTTask::FAILURE); // * elapsed 1.2
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::FRESH, 1, 4, 1); // * cancelled & exited
|
||||
}
|
||||
SUBCASE("When finishing on time") {
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(lim->execute(0.1) == BTTask::SUCCESS); // * elapsed 0.9
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 1, 4, 1); // * succeeded & exited
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("With a quick task") {
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(lim->execute(0.01666) == BTTask::SUCCESS);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 1, 1, 1); // * succeeded
|
||||
|
||||
task->ret_status = BTTask::FAILURE;
|
||||
CHECK(lim->execute(0.01666) == BTTask::FAILURE);
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::FAILURE, 2, 2, 2); // * failed
|
||||
}
|
||||
|
||||
SUBCASE("If time limit is reset") {
|
||||
task->ret_status = BTTask::RUNNING;
|
||||
|
||||
CHECK(lim->execute(0.0) == BTTask::RUNNING); // * elapsed 0.0
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::RUNNING, 1, 1, 0); // * running
|
||||
CHECK(lim->execute(1.1) == BTTask::FAILURE); // * elapsed 1.1
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::FRESH, 1, 2, 1); // * cancelled due to time limit exceeded
|
||||
|
||||
CHECK(lim->execute(0.0) == BTTask::RUNNING); // * elapsed 0.0
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::RUNNING, 2, 3, 1); // * running
|
||||
CHECK(lim->execute(0.8) == BTTask::RUNNING); // * elapsed 0.8
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::RUNNING, 2, 4, 1); // * running
|
||||
|
||||
task->ret_status = BTTask::SUCCESS;
|
||||
CHECK(lim->execute(0.8) == BTTask::SUCCESS); // * elapsed 1.6
|
||||
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 2, 5, 2); // * succeeded, despite time limit exceeded
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestTimeLimit
|
||||
|
||||
#endif // TEST_TIME_LIMIT_H
|
|
@ -0,0 +1,120 @@
|
|||
/**
|
||||
* test_wait_actions.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_WAIT_ACTIONS_H
|
||||
#define TEST_WAIT_ACTIONS_H
|
||||
|
||||
#include "limbo_test.h"
|
||||
|
||||
#include "modules/limboai/bt/tasks/bt_task.h"
|
||||
#include "modules/limboai/bt/tasks/utility/bt_random_wait.h"
|
||||
#include "modules/limboai/bt/tasks/utility/bt_wait.h"
|
||||
#include "modules/limboai/bt/tasks/utility/bt_wait_ticks.h"
|
||||
|
||||
#include "core/math/math_funcs.h"
|
||||
|
||||
namespace TestWaitActions {
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTWait") {
|
||||
Ref<BTWait> wait = memnew(BTWait);
|
||||
|
||||
SUBCASE("With zero duration") {
|
||||
wait->set_duration(0.0);
|
||||
CHECK(wait->execute(0.0) == BTWait::SUCCESS);
|
||||
}
|
||||
SUBCASE("With one second duration") {
|
||||
wait->set_duration(1.0);
|
||||
CHECK(wait->execute(0.0) == BTWait::RUNNING);
|
||||
CHECK(wait->execute(0.5) == BTWait::RUNNING); // * elapsed 0.5
|
||||
CHECK(wait->execute(0.5) == BTWait::SUCCESS); // * elapsed 1.0
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTWaitTicks") {
|
||||
Ref<BTWaitTicks> wait = memnew(BTWaitTicks);
|
||||
|
||||
SUBCASE("With zero ticks") {
|
||||
wait->set_num_ticks(0);
|
||||
CHECK(wait->execute(0.01666) == BTWait::SUCCESS); // * elapsed 0 ticks
|
||||
}
|
||||
SUBCASE("With 1 tick") {
|
||||
wait->set_num_ticks(1);
|
||||
CHECK(wait->execute(0.01666) == BTWait::RUNNING); // * elapsed 0 ticks
|
||||
CHECK(wait->execute(0.01666) == BTWait::SUCCESS); // * elapsed 1 tick
|
||||
}
|
||||
SUBCASE("With 2 ticks") {
|
||||
wait->set_num_ticks(2);
|
||||
CHECK(wait->execute(0.01666) == BTWait::RUNNING); // * elapsed 0 ticks
|
||||
CHECK(wait->execute(0.01666) == BTWait::RUNNING); // * elapsed 1 tick
|
||||
CHECK(wait->execute(0.01666) == BTWait::SUCCESS); // * elapsed 2 ticks
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[Modules][LimboAI] BTRandomWait") {
|
||||
Ref<BTRandomWait> wait = memnew(BTRandomWait);
|
||||
|
||||
Math::randomize();
|
||||
|
||||
SUBCASE("With duration range [0, 0]") {
|
||||
wait->set_min_duration(0.0);
|
||||
wait->set_max_duration(0.0);
|
||||
CHECK(wait->execute(0.01666) == BTWait::SUCCESS);
|
||||
}
|
||||
SUBCASE("With certain SUCCESS") {
|
||||
wait->set_min_duration(0.5);
|
||||
wait->set_max_duration(1.0);
|
||||
|
||||
CHECK(wait->execute(0.00) == BTWait::RUNNING); // * elapsed 0.00
|
||||
CHECK(wait->execute(0.25) == BTWait::RUNNING); // * elapsed 0.25
|
||||
CHECK(wait->execute(0.76) == BTWait::SUCCESS); // * elapsed 1.01
|
||||
}
|
||||
SUBCASE("With duration range [0.5, 1.0]") {
|
||||
wait->set_min_duration(0.5);
|
||||
wait->set_max_duration(1.0);
|
||||
|
||||
int num_successes = 0;
|
||||
int num_running = 0;
|
||||
int num_failures = 0;
|
||||
int num_undefined = 0;
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
wait->execute(0.00); // * elapsed 0.00
|
||||
wait->execute(0.75); // * elapsed 0.75
|
||||
switch (wait->get_status()) {
|
||||
case BTTask::RUNNING: {
|
||||
num_running += 1;
|
||||
} break;
|
||||
case BTTask::SUCCESS: {
|
||||
num_successes += 1;
|
||||
} break;
|
||||
case BTTask::FAILURE: {
|
||||
num_failures += 1;
|
||||
} break;
|
||||
default: {
|
||||
num_undefined += 1;
|
||||
} break;
|
||||
}
|
||||
wait->cancel();
|
||||
}
|
||||
|
||||
// * Expected ~500/500 SUCCESS/RUNNING.
|
||||
CHECK(num_successes > 450);
|
||||
CHECK(num_successes < 550);
|
||||
CHECK(num_running > 450);
|
||||
CHECK(num_running < 550);
|
||||
CHECK(num_failures == 0);
|
||||
CHECK(num_undefined == 0);
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestWaitActions
|
||||
|
||||
#endif // TEST_WAIT_ACTIONS_H
|
Loading…
Reference in New Issue