diff --git a/blackboard/blackboard.cpp b/blackboard/blackboard.cpp index 5d15bd1..99fdda5 100644 --- a/blackboard/blackboard.cpp +++ b/blackboard/blackboard.cpp @@ -106,7 +106,7 @@ void Blackboard::bind_var_to_property(const StringName &p_name, Object *p_object } void Blackboard::unbind_var(const StringName &p_name) { - ERR_FAIL_COND_MSG(data.has(p_name), "Blackboard: Can't unbind variable that doesn't exist (var: " + p_name + ")."); + ERR_FAIL_COND_MSG(!data.has(p_name), "Blackboard: Can't unbind variable that doesn't exist (var: " + p_name + ")."); data[p_name].unbind(); } @@ -128,7 +128,7 @@ void Blackboard::link_var(const StringName &p_name, const Ref &p_tar } void Blackboard::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_var", "var_name", "default", "complain"), &Blackboard::get_var, Variant(), true); + ClassDB::bind_method(D_METHOD("get_var", "var_name", "default", "complain"), &Blackboard::get_var, DEFVAL(Variant()), DEFVAL(true)); ClassDB::bind_method(D_METHOD("set_var", "var_name", "value"), &Blackboard::set_var); ClassDB::bind_method(D_METHOD("has_var", "var_name"), &Blackboard::has_var); ClassDB::bind_method(D_METHOD("set_parent", "blackboard"), &Blackboard::set_parent); diff --git a/blackboard/blackboard.h b/blackboard/blackboard.h index 5e60ed7..49288e1 100644 --- a/blackboard/blackboard.h +++ b/blackboard/blackboard.h @@ -46,7 +46,7 @@ public: Ref top() const; - Variant get_var(const StringName &p_name, const Variant &p_default, bool p_complain = true) const; + Variant get_var(const StringName &p_name, const Variant &p_default = Variant(), bool p_complain = true) const; void set_var(const StringName &p_name, const Variant &p_value); bool has_var(const StringName &p_name) const; void erase_var(const StringName &p_name); diff --git a/tests/test_blackboard.h b/tests/test_blackboard.h new file mode 100644 index 0000000..5605129 --- /dev/null +++ b/tests/test_blackboard.h @@ -0,0 +1,186 @@ +#ifndef TEST_BLACKBOARD_H +#define TEST_BLACKBOARD_H + +#include "core/variant/variant.h" +#include "limbo_test.h" + +#include "modules/limboai/blackboard/blackboard.h" + +namespace TestBlackboard { + +class TestPropertyHolder : public RefCounted { + GDCLASS(TestPropertyHolder, RefCounted); + +public: + int property = 0; + + void set_property(int p_property) { property = p_property; } + int get_property() { return property; } + + static void _bind_methods() { + ClassDB::bind_method(D_METHOD("set_property", "property"), &TestPropertyHolder::set_property); + ClassDB::bind_method(D_METHOD("get_property"), &TestPropertyHolder::get_property); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "property"), "set_property", "get_property"); + } +}; + +TEST_CASE("[Modules][LimboAI] Test Blackboard") { + Ref blackboard = memnew(Blackboard); + + blackboard->set_var("a", 1); + blackboard->set_var("b", Vector2(2, 2)); + blackboard->set_var("c", String("3")); + + Variant not_found("not_found"); + + SUBCASE("Test getter") { + CHECK_EQ(blackboard->get_var("a", not_found), Variant(1)); + CHECK_EQ(blackboard->get_var("b", not_found), Variant(Vector2(2, 2))); + CHECK_EQ(blackboard->get_var("c", not_found), Variant("3")); + } + + SUBCASE("Test has_var()") { + CHECK(blackboard->has_var("a")); + CHECK(blackboard->has_var("b")); + CHECK(blackboard->has_var("c")); + CHECK_FALSE(blackboard->has_var("d")); + } + + SUBCASE("Test erase_var()") { + blackboard->erase_var("b"); + CHECK_FALSE(blackboard->has_var("b")); + CHECK(blackboard->has_var("a")); + CHECK(blackboard->has_var("c")); + } + + SUBCASE("Test clear()") { + blackboard->clear(); + CHECK_FALSE(blackboard->has_var("a")); + CHECK_FALSE(blackboard->has_var("b")); + CHECK_FALSE(blackboard->has_var("c")); + } + + SUBCASE("Test list_vars()") { + TypedArray vars = blackboard->list_vars(); + CHECK_EQ(vars.size(), 3); + CHECK(vars.has("a")); + CHECK(vars.has("b")); + CHECK(vars.has("c")); + } + + SUBCASE("Test get_vars_as_dict()") { + Dictionary dict = blackboard->get_vars_as_dict(); + CHECK_EQ(dict.size(), 3); + CHECK(dict.has("a")); + CHECK(dict.has("b")); + CHECK(dict.has("c")); + CHECK_EQ(dict["a"], Variant(1)); + CHECK_EQ(dict["b"], Variant(Vector2(2, 2))); + CHECK_EQ(dict["c"], Variant("3")); + + // * Should not include parent scope values + Ref parent_scope = memnew(Blackboard); + blackboard->set_parent(parent_scope); + parent_scope->set_var("d", 1); + dict = blackboard->get_vars_as_dict(); + CHECK_EQ(dict.size(), 3); + CHECK_FALSE(dict.has("d")); + } + + SUBCASE("Test populate_from_dict() overriding values") { + Dictionary dict; + dict["a"] = "value_a"; + dict["b"] = "value_b"; + dict["c"] = "value_c"; + blackboard->populate_from_dict(dict); + + CHECK_EQ(blackboard->list_vars().size(), 3); + CHECK_EQ(blackboard->get_var("a", not_found), Variant("value_a")); + CHECK_EQ(blackboard->get_var("b", not_found), Variant("value_b")); + CHECK_EQ(blackboard->get_var("c", not_found), Variant("value_c")); + } + + SUBCASE("Test populate_from_dict() creating new values") { + Dictionary dict; + dict["d"] = "value_d"; + dict["e"] = "value_e"; + blackboard->populate_from_dict(dict); + + CHECK_EQ(blackboard->list_vars().size(), 5); + CHECK_EQ(blackboard->get_var("d", not_found), Variant("value_d")); + CHECK_EQ(blackboard->get_var("e", not_found), Variant("value_e")); + } + + SUBCASE("Test scoping") { + CHECK_EQ(blackboard->top(), blackboard); + + Ref parent_scope = memnew(Blackboard); + blackboard->set_parent(parent_scope); + CHECK_EQ(blackboard->get_parent(), parent_scope); + CHECK_EQ(blackboard->top(), parent_scope); + + parent_scope->set_var("a", 5); + CHECK_EQ(blackboard->get_var("a", not_found), Variant(1)); // * should return current scope value + CHECK_EQ(parent_scope->get_var("a", not_found), Variant(5)); // * should return parent scope value + CHECK_EQ(parent_scope->get_var("b", not_found, false), not_found); // * should not return current scope value + + parent_scope->set_var("d", 123); + CHECK_EQ(blackboard->get_var("d", not_found), Variant(123)); // * should return parent scope value + + blackboard->set_var("d", 456); + CHECK_EQ(blackboard->get_var("d", not_found), Variant(456)); // * should return new value + CHECK_EQ(parent_scope->get_var("d", not_found), Variant(123)); // * should return parent scope value + + Ref grand_parent_scope = memnew(Blackboard); + parent_scope->set_parent(grand_parent_scope); + CHECK_EQ(blackboard->top(), grand_parent_scope); + + grand_parent_scope->set_var("a", "value_found"); + CHECK_EQ(grand_parent_scope->get_var("a", not_found), Variant("value_found")); + CHECK_EQ(grand_parent_scope->get_var("b", not_found, false), not_found); + CHECK_EQ(grand_parent_scope->get_var("c", not_found, false), not_found); + CHECK_EQ(grand_parent_scope->get_var("d", not_found, false), not_found); + CHECK_EQ(parent_scope->get_var("a", not_found), Variant(5)); + CHECK_EQ(blackboard->get_var("a", not_found), Variant(1)); + } + + SUBCASE("Test binding") { + Ref holder = memnew(TestPropertyHolder); + blackboard->bind_var_to_property("a", holder.ptr(), "property"); + + holder->set_property(5); + CHECK_EQ(blackboard->get_var("a", not_found), Variant(5)); + blackboard->set_var("a", Variant(6)); + CHECK_EQ(holder->get_property(), 6); + + blackboard->unbind_var("a"); + blackboard->set_var("a", Variant(7)); + CHECK_EQ(holder->get_property(), 6); + CHECK_EQ(blackboard->get_var("a", not_found), Variant(7)); + } + + SUBCASE("Test linking") { + Ref target_blackboard = memnew(Blackboard); + + target_blackboard->set_var("aa", Variant(111)); + blackboard->link_var("a", target_blackboard, "aa"); + CHECK(target_blackboard->has_var("aa")); + CHECK_FALSE(target_blackboard->has_var("a")); + + CHECK_EQ(blackboard->get_var("a", not_found), Variant(111)); + CHECK_EQ(target_blackboard->get_var("aa", not_found), Variant(111)); + + blackboard->set_var("a", Variant(222)); + CHECK_EQ(blackboard->get_var("a", not_found), Variant(222)); + CHECK_EQ(target_blackboard->get_var("aa", not_found), Variant(222)); + + target_blackboard->set_var("aa", Variant(333)); + CHECK_EQ(blackboard->get_var("a", not_found), Variant(333)); + CHECK_EQ(target_blackboard->get_var("aa", not_found), Variant(333)); + } +} + +} //namespace TestBlackboard + +#endif // TEST_BLACKBOARD_H