diff --git a/tests/test_task.h b/tests/test_task.h new file mode 100644 index 0000000..e2b7224 --- /dev/null +++ b/tests/test_task.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 task = memnew(BTTask); + Ref child1 = memnew(BTTask); + Ref 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 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 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 other = memnew(BTTask); + CHECK(task->get_child_index(other) == -1); + } + SUBCASE("Test is_descendant_of()") { + Ref 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 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 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 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 task = memnew(BTTestAction); + Ref child1 = memnew(BTTestAction); + Ref 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 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