summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@gmail.com>2025-05-13 14:19:42 +0200
committerFelix Morgner <felix.morgner@gmail.com>2025-05-13 14:19:42 +0200
commit661e98cf8bb61f29049d405aef9cdaace1449ac8 (patch)
treec7217b59a5fe544ce620483cc08f21b94406d218 /lib
parentfb015121f064f2be12d1e28cdc7c54e074ed0411 (diff)
downloadturns-661e98cf8bb61f29049d405aef9cdaace1449ac8.tar.xz
turns-661e98cf8bb61f29049d405aef9cdaace1449ac8.zip
libmm: add tests
Diffstat (limited to 'lib')
-rw-r--r--lib/CMakeLists.txt27
-rw-r--r--lib/src/turnsmm/enums.cpp38
-rw-r--r--lib/src/turnsmm/enums.hpp37
-rw-r--r--lib/src/turnsmm/init.cpp28
-rw-r--r--lib/src/turnsmm/init.hpp9
-rw-r--r--lib/src/turnsmm/participant.cpp66
-rw-r--r--lib/src/turnsmm/participant.hpp19
-rw-r--r--lib/tests/turnsmm/participant.cpp447
-rw-r--r--lib/tests/turnsmm/runtime_init.cpp25
9 files changed, 683 insertions, 13 deletions
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 0fd7aa0..e5a4916 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -64,6 +64,8 @@ install(TARGETS "lib"
# C++ Wrapper
set(CXX_HEADERS
+ "src/turnsmm/enums.hpp"
+ "src/turnsmm/init.hpp"
"src/turnsmm/participant.hpp"
"src/turnsmm.hpp"
@@ -71,6 +73,8 @@ set(CXX_HEADERS
)
set(CXX_SOURCES
+ "src/turnsmm/enums.cpp"
+ "src/turnsmm/init.cpp"
"src/turnsmm/participant.cpp"
)
@@ -131,6 +135,27 @@ target_link_options("lib-tests" PRIVATE
"$<$<AND:$<CXX_COMPILER_ID:GNU,Clang>,$<CONFIG:Debug>>:--coverage>"
)
+catch_discover_tests("lib-tests")
+
+# C++ Wrapper Tests
+
+add_executable("libmm-tests"
+ "tests/turnsmm/runtime_init.cpp"
+ "tests/turnsmm/participant.cpp"
+)
+
+target_link_libraries("libmm-tests" PRIVATE
+ "Catch2::Catch2WithMain"
+
+ "turns::mm"
+)
+
+target_link_options("libmm-tests" PRIVATE
+ "$<$<AND:$<CXX_COMPILER_ID:GNU,Clang>,$<CONFIG:Debug>>:--coverage>"
+)
+
+catch_discover_tests("libmm-tests")
+
# file(GLOB_RECURSE TEST_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/tests" CONFIGURE_DEPENDS "*.trns")
# target_add_glib_resources("core-tests"
@@ -138,5 +163,3 @@ target_link_options("lib-tests" PRIVATE
# WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/tests"
# CSS_FILES ${TEST_FILES}
# )
-
-catch_discover_tests("lib-tests") \ No newline at end of file
diff --git a/lib/src/turnsmm/enums.cpp b/lib/src/turnsmm/enums.cpp
new file mode 100644
index 0000000..91d6eb3
--- /dev/null
+++ b/lib/src/turnsmm/enums.cpp
@@ -0,0 +1,38 @@
+#include "turnsmm/enums.hpp"
+
+#include "turns-disposition.h"
+#include "turns-enums.h" // IWYU pragma: keep
+
+#include <glib-object.h>
+
+#include <type_traits>
+
+namespace
+{
+ template<auto Wrapped, auto Unwrapped>
+ auto constexpr matches =
+ static_cast<std::underlying_type_t<decltype(Wrapped)>>(Wrapped) == static_cast<std::underlying_type_t<decltype(Unwrapped)>>(Unwrapped);
+} // namespace
+
+namespace Turns
+{
+
+ static_assert(matches<Disposition::Neutral, TURNS_DISPOSITION_NEUTRAL>);
+ static_assert(matches<Disposition::Friendly, TURNS_DISPOSITION_FRIENDLY>);
+ static_assert(matches<Disposition::Hostile, TURNS_DISPOSITION_HOSTILE>);
+ static_assert(matches<Disposition::Secret, TURNS_DISPOSITION_SECRET>);
+
+} // namespace Turns
+
+namespace Glib
+{
+#define VALUE_SPECIALIZATION(Enum, TurnsEnumName) \
+ auto Value<Turns::Enum>::value_type() -> GType \
+ { \
+ return turns_##TurnsEnumName##_get_type(); \
+ }
+
+ VALUE_SPECIALIZATION(Disposition, disposition)
+
+#undef VALUE_SPECIALIZATION
+} // namespace Glib \ No newline at end of file
diff --git a/lib/src/turnsmm/enums.hpp b/lib/src/turnsmm/enums.hpp
new file mode 100644
index 0000000..f99a6f1
--- /dev/null
+++ b/lib/src/turnsmm/enums.hpp
@@ -0,0 +1,37 @@
+#ifndef TURNSMM_ENUMS_HPP
+#define TURNSMM_ENUMS_HPP
+
+#include <glibmm/value.h>
+
+#include <glib-object.h>
+
+namespace Turns
+{
+ enum struct Disposition
+ {
+ Neutral,
+ Friendly,
+ Hostile,
+ Secret,
+ };
+
+} // namespace Turns
+
+namespace Glib
+{
+
+#define VALUE_SPECIALIZATION(Enum) \
+ template<> \
+ class Value<Turns::Enum> : public Glib::Value_Enum<Turns::Enum> \
+ { \
+ public: \
+ auto static value_type() -> GType; \
+ }
+
+ VALUE_SPECIALIZATION(Disposition);
+
+#undef VALUE_SPECIALIZATION
+
+} // namespace Glib
+
+#endif \ No newline at end of file
diff --git a/lib/src/turnsmm/init.cpp b/lib/src/turnsmm/init.cpp
new file mode 100644
index 0000000..77c217b
--- /dev/null
+++ b/lib/src/turnsmm/init.cpp
@@ -0,0 +1,28 @@
+#include "turnsmm/init.hpp"
+
+#include "turns-init.h"
+#include "turnsmm/enums.hpp"
+#include "turnsmm/participant.hpp"
+#include "turnsmm/private/participant_p.hpp" // IWYU pragma: keep
+
+#include <glibmm/value.h>
+#include <glibmm/wrap.h>
+
+#include <glib-object.h>
+
+#define WRAP_CLASS(Name, name) Glib::wrap_register(turns_##name##_get_type(), &Name##_Class::wrap_new)
+#define ENSURE_TYPE(Name) g_type_ensure(Name::get_type())
+
+namespace Turns
+{
+
+ auto init() -> void
+ {
+ turns_init();
+
+ WRAP_CLASS(Participant, participant);
+
+ ENSURE_TYPE(Participant);
+ }
+
+} // namespace Turns \ No newline at end of file
diff --git a/lib/src/turnsmm/init.hpp b/lib/src/turnsmm/init.hpp
new file mode 100644
index 0000000..a9ee8cc
--- /dev/null
+++ b/lib/src/turnsmm/init.hpp
@@ -0,0 +1,9 @@
+#ifndef TURNSMM_INIT_HPP
+#define TURNSMM_INIT_HPP
+
+namespace Turns
+{
+ void init(void);
+}
+
+#endif
diff --git a/lib/src/turnsmm/participant.cpp b/lib/src/turnsmm/participant.cpp
index 0086ada..5cc3db6 100644
--- a/lib/src/turnsmm/participant.cpp
+++ b/lib/src/turnsmm/participant.cpp
@@ -2,12 +2,14 @@
#include "turns-disposition.h"
#include "turns-participant.h"
+#include "turnsmm/enums.hpp"
#include "turnsmm/private/participant_p.hpp"
#include <glibmm/class.h>
#include <glibmm/object.h>
#include <glibmm/objectbase.h>
#include <glibmm/private/object_p.h>
+#include <glibmm/propertyproxy.h>
#include <glibmm/refptr.h>
#include <glibmm/ustring.h>
#include <glibmm/utility.h>
@@ -23,8 +25,6 @@ namespace Turns
namespace
{
auto constinit _class = Participant_Class{};
-
- auto constexpr type_name = "TurnsParticipant";
} // namespace
auto Participant_Class::init() -> Glib::Class const &
@@ -49,13 +49,13 @@ namespace Turns
}
Participant::Participant()
- : Glib::ObjectBase{type_name}
+ : Glib::ObjectBase{nullptr}
, Glib::Object{Glib::ConstructParams{_class.init()}}
{
}
- Participant::Participant(Glib::ustring const & name, float priority, int disposition)
- : Glib::ObjectBase{type_name}
+ Participant::Participant(Glib::ustring const & name, float priority, Disposition disposition)
+ : Glib::ObjectBase{nullptr}
, Glib::Object{Glib::ConstructParams{_class.init(), "name", name.c_str(), "priority", priority, "disposition", disposition, nullptr}}
{
}
@@ -86,9 +86,9 @@ namespace Turns
return turns_participant_get_defeated(const_cast<BaseObjectType *>(unwrap(this)));
}
- auto Participant::get_disposition() const noexcept -> int
+ auto Participant::get_disposition() const noexcept -> Disposition
{
- return turns_participant_get_disposition(const_cast<BaseObjectType *>(unwrap(this)));
+ return static_cast<Disposition>(turns_participant_get_disposition(const_cast<BaseObjectType *>(unwrap(this))));
}
auto Participant::get_name() const -> Glib::ustring
@@ -111,7 +111,7 @@ namespace Turns
return turns_participant_set_defeated(unwrap(this), value);
}
- auto Participant::set_disposition(int value) noexcept -> void
+ auto Participant::set_disposition(Disposition value) noexcept -> void
{
return turns_participant_set_disposition(unwrap(this), static_cast<TurnsDisposition>(value));
}
@@ -126,6 +126,56 @@ namespace Turns
return turns_participant_set_priority(unwrap(this), value);
}
+ auto Participant::property_active() const noexcept -> Glib::PropertyProxy_ReadOnly<bool>
+ {
+ return {this, "active"};
+ }
+
+ auto Participant::property_active() noexcept -> Glib::PropertyProxy<bool>
+ {
+ return {this, "active"};
+ }
+
+ auto Participant::property_defeated() const noexcept -> Glib::PropertyProxy_ReadOnly<bool>
+ {
+ return {this, "defeated"};
+ }
+
+ auto Participant::property_defeated() noexcept -> Glib::PropertyProxy<bool>
+ {
+ return {this, "defeated"};
+ }
+
+ auto Participant::property_disposition() const noexcept -> Glib::PropertyProxy_ReadOnly<Disposition>
+ {
+ return {this, "disposition"};
+ }
+
+ auto Participant::property_disposition() noexcept -> Glib::PropertyProxy<Disposition>
+ {
+ return {this, "disposition"};
+ }
+
+ auto Participant::property_name() const noexcept -> Glib::PropertyProxy_ReadOnly<Glib::ustring const &>
+ {
+ return {this, "name"};
+ }
+
+ auto Participant::property_name() noexcept -> Glib::PropertyProxy<Glib::ustring const &>
+ {
+ return {this, "name"};
+ }
+
+ auto Participant::property_priority() const noexcept -> Glib::PropertyProxy_ReadOnly<float>
+ {
+ return {this, "priority"};
+ }
+
+ auto Participant::property_priority() noexcept -> Glib::PropertyProxy<float>
+ {
+ return {this, "priority"};
+ }
+
Participant::Participant(BaseObjectType * gobj)
: Glib::Object((GObject *)gobj)
{
diff --git a/lib/src/turnsmm/participant.hpp b/lib/src/turnsmm/participant.hpp
index 1eb5c1e..b41c64b 100644
--- a/lib/src/turnsmm/participant.hpp
+++ b/lib/src/turnsmm/participant.hpp
@@ -2,8 +2,10 @@
#define TURNSMM_PARTICIPANT_HPP
#include "turns-participant.h"
+#include "turnsmm/enums.hpp"
#include <glibmm/object.h>
+#include <glibmm/propertyproxy.h>
#include <glibmm/refptr.h>
#include <glibmm/ustring.h>
@@ -19,7 +21,7 @@ namespace Turns
using CppObjectType = Participant;
Participant();
- Participant(Glib::ustring const & name, float priority, int disposition);
+ Participant(Glib::ustring const & name, float priority, Disposition disposition);
[[nodiscard]] auto gobj() noexcept -> BaseObjectType *;
[[nodiscard]] auto gobj() const -> BaseObjectType const *;
@@ -27,16 +29,27 @@ namespace Turns
[[nodiscard]] auto get_active() const noexcept -> bool;
[[nodiscard]] auto get_defeated() const noexcept -> bool;
- [[nodiscard]] auto get_disposition() const noexcept -> int;
+ [[nodiscard]] auto get_disposition() const noexcept -> Disposition;
[[nodiscard]] auto get_name() const -> Glib::ustring;
[[nodiscard]] auto get_priority() const noexcept -> float;
auto set_active(bool value) noexcept -> void;
auto set_defeated(bool value) noexcept -> void;
- auto set_disposition(int value) noexcept -> void;
+ auto set_disposition(Disposition value) noexcept -> void;
auto set_name(Glib::ustring const & value) noexcept -> void;
auto set_priority(float value) noexcept -> void;
+ [[nodiscard]] auto property_active() const noexcept -> Glib::PropertyProxy_ReadOnly<bool>;
+ [[nodiscard]] auto property_active() noexcept -> Glib::PropertyProxy<bool>;
+ [[nodiscard]] auto property_defeated() const noexcept -> Glib::PropertyProxy_ReadOnly<bool>;
+ [[nodiscard]] auto property_defeated() noexcept -> Glib::PropertyProxy<bool>;
+ [[nodiscard]] auto property_disposition() const noexcept -> Glib::PropertyProxy_ReadOnly<Disposition>;
+ [[nodiscard]] auto property_disposition() noexcept -> Glib::PropertyProxy<Disposition>;
+ [[nodiscard]] auto property_name() const noexcept -> Glib::PropertyProxy_ReadOnly<Glib::ustring const &>;
+ [[nodiscard]] auto property_name() noexcept -> Glib::PropertyProxy<Glib::ustring const &>;
+ [[nodiscard]] auto property_priority() const noexcept -> Glib::PropertyProxy_ReadOnly<float>;
+ [[nodiscard]] auto property_priority() noexcept -> Glib::PropertyProxy<float>;
+
protected:
friend Participant_Class;
diff --git a/lib/tests/turnsmm/participant.cpp b/lib/tests/turnsmm/participant.cpp
new file mode 100644
index 0000000..d1e1762
--- /dev/null
+++ b/lib/tests/turnsmm/participant.cpp
@@ -0,0 +1,447 @@
+#include "turnsmm/participant.hpp"
+
+#include "turnsmm/enums.hpp"
+
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators.hpp>
+
+#include <glibmm/ustring.h>
+
+#include <glib-object.h>
+#include <glib.h>
+
+SCENARIO("Creating a participant", "[lib][object][lifetime]")
+{
+ GIVEN("A participant constructed using turns_participant_new()")
+ {
+ auto instance = Turns::Participant{};
+
+ THEN("it's name is empty")
+ {
+ REQUIRE(instance.get_name().empty());
+ }
+
+ THEN("it's priority is 0.0f")
+ {
+ REQUIRE(instance.get_priority() == Catch::Approx{0.0});
+ }
+
+ THEN("it's disposition is neutral")
+ {
+ REQUIRE(instance.get_disposition() == Turns::Disposition::Neutral);
+ }
+
+ THEN("it's active state is false")
+ {
+ REQUIRE_FALSE(instance.get_active());
+ }
+
+ THEN("it's defeated state is false")
+ {
+ REQUIRE_FALSE(instance.get_defeated());
+ }
+ }
+
+ GIVEN("A participant constructed using turns_participant_new_with(...)")
+ {
+ auto name = "Test Participant";
+ auto priority = 12.4f;
+ auto disposition = Turns::Disposition::Friendly;
+ auto instance = Turns::Participant(name, priority, disposition);
+
+ THEN("it's name is set")
+ {
+ REQUIRE(instance.get_name() == name);
+ }
+
+ THEN("it's priority is set")
+ {
+ REQUIRE(instance.get_priority() == Catch::Approx{priority});
+ }
+
+ THEN("it's disposition is")
+ {
+ REQUIRE(instance.get_disposition() == disposition);
+ }
+
+ THEN("it's active state is false")
+ {
+ REQUIRE_FALSE(instance.get_active());
+ }
+
+ THEN("it's defeated state is false")
+ {
+ REQUIRE_FALSE(instance.get_defeated());
+ }
+ }
+}
+
+SCENARIO("Modifying a participant", "[lib][object][data]")
+{
+ GIVEN("A default participant instance")
+ {
+ auto instance = Turns::Participant{};
+
+ WHEN("a new name is set")
+ {
+ auto new_value = "Test Participant";
+
+ CHECK(instance.get_name().empty());
+ CHECK_FALSE(instance.get_name() == new_value);
+ instance.set_name(new_value);
+
+ THEN("it's name is not empty")
+ {
+ REQUIRE_FALSE(instance.get_name().empty());
+ }
+
+ THEN("it's name has the new value")
+ {
+ REQUIRE(instance.get_name() == new_value);
+ }
+ }
+
+ WHEN("a new priority is set")
+ {
+ auto new_value = GENERATE(-8.0f, 12.0f, 4.0f);
+
+ CHECK_FALSE(instance.get_priority() == new_value);
+ instance.set_priority(new_value);
+
+ THEN("it's priority has the new value")
+ {
+ REQUIRE(instance.get_priority() == Catch::Approx{new_value});
+ }
+ }
+
+ WHEN("a new disposition is set")
+ {
+ auto new_value = GENERATE(Turns::Disposition::Friendly, Turns::Disposition::Hostile, Turns::Disposition::Secret);
+
+ CHECK_FALSE(instance.get_disposition() == new_value);
+ instance.set_disposition(new_value);
+
+ THEN("it's disposition has the new value")
+ {
+ REQUIRE(instance.get_disposition() == new_value);
+ }
+ }
+
+ WHEN("a new active state is set")
+ {
+ auto new_value = true;
+
+ CHECK_FALSE(instance.get_active() == new_value);
+ instance.set_active(new_value);
+
+ THEN("it's active state has the new value")
+ {
+ REQUIRE(instance.get_active() == new_value);
+ }
+ }
+
+ WHEN("a new defeated state is set")
+ {
+ auto new_value = true;
+
+ CHECK_FALSE(instance.get_defeated() == new_value);
+ instance.set_defeated(new_value);
+
+ THEN("it's defeated state has the new value")
+ {
+ REQUIRE(instance.get_defeated() == new_value);
+ }
+ }
+
+ AND_GIVEN("a signal handler has been subscribed to the name property")
+ {
+ auto was_notified = false;
+ instance.property_name().signal_changed().connect([&] { was_notified = true; });
+
+ WHEN("a new name is set using set_name")
+ {
+ auto new_value = "Test Participant";
+
+ CHECK_FALSE(instance.get_name() == new_value);
+ instance.set_name(new_value);
+
+ THEN("a notification is issued")
+ {
+ REQUIRE(was_notified);
+ }
+ }
+
+ WHEN("the same name is set using set_name")
+ {
+ auto new_value = instance.get_name();
+
+ CHECK(instance.get_name() == new_value);
+ instance.set_name(new_value);
+
+ THEN("no notification is issued")
+ {
+ REQUIRE_FALSE(was_notified);
+ }
+ }
+
+ WHEN("a new name is set using Glib::Object::set_property")
+ {
+ auto new_value = "Test Participant";
+
+ CHECK_FALSE(instance.get_name() == new_value);
+ instance.set_property("name", Glib::ustring{new_value});
+
+ THEN("a notification is issued")
+ {
+ REQUIRE(was_notified);
+ }
+ }
+
+ WHEN("the same name is set using Glib::Object::set_property")
+ {
+ auto new_value = instance.get_name();
+
+ CHECK(instance.get_name() == new_value);
+ instance.set_property("name", new_value);
+
+ THEN("no notification is issued")
+ {
+ REQUIRE_FALSE(was_notified);
+ }
+ }
+ }
+
+ AND_GIVEN("a signal handler has been subscribed to the priority property")
+ {
+ auto was_notified = false;
+ instance.property_priority().signal_changed().connect([&] { was_notified = true; });
+
+ WHEN("a new priority is set using set_priority")
+ {
+ auto new_value = 8.4;
+
+ CHECK_FALSE(instance.get_priority() == new_value);
+ instance.set_priority(new_value);
+
+ THEN("a notification is issued")
+ {
+ REQUIRE(was_notified);
+ }
+ }
+
+ WHEN("the same priority is set using set_priority")
+ {
+ auto new_value = instance.get_priority();
+
+ CHECK(instance.get_priority() == new_value);
+ instance.set_priority(new_value);
+
+ THEN("no notification is issued")
+ {
+ REQUIRE_FALSE(was_notified);
+ }
+ }
+
+ WHEN("a new priority is set using Glib::Object::set_property")
+ {
+ auto new_value = 8.4;
+
+ CHECK_FALSE(instance.get_priority() == new_value);
+ instance.set_property("priority", new_value);
+
+ THEN("a notification is issued")
+ {
+ REQUIRE(was_notified);
+ }
+ }
+
+ WHEN("the same priority is set using Glib::Object::set_property")
+ {
+ auto new_value = instance.get_priority();
+
+ CHECK(instance.get_priority() == new_value);
+ instance.set_property("priority", new_value);
+
+ THEN("no notification is issued")
+ {
+ REQUIRE_FALSE(was_notified);
+ }
+ }
+ }
+
+ AND_GIVEN("a signal handler has been subscribed to the disposition property")
+ {
+ auto was_notified = false;
+ instance.property_disposition().signal_changed().connect([&] { was_notified = true; });
+
+ WHEN("a new disposition is set using set_disposition")
+ {
+ auto new_value = Turns::Disposition::Hostile;
+
+ CHECK_FALSE(instance.get_disposition() == new_value);
+ instance.set_disposition(new_value);
+
+ THEN("a notification is issued")
+ {
+ REQUIRE(was_notified);
+ }
+ }
+
+ WHEN("the same disposition is set using set_disposition")
+ {
+ auto new_value = instance.get_disposition();
+
+ CHECK(instance.get_disposition() == new_value);
+ instance.set_disposition(new_value);
+
+ THEN("no notification is issued")
+ {
+ REQUIRE_FALSE(was_notified);
+ }
+ }
+
+ WHEN("a new disposition is set using Glib::Object::set_property")
+ {
+ auto new_value = Turns::Disposition::Secret;
+
+ CHECK_FALSE(instance.get_disposition() == new_value);
+ instance.set_property("disposition", new_value);
+
+ THEN("a notification is issued")
+ {
+ REQUIRE(was_notified);
+ }
+ }
+
+ WHEN("the same disposition is set using Glib::Object::set_property")
+ {
+ auto new_value = instance.get_disposition();
+
+ CHECK(instance.get_disposition() == new_value);
+ instance.set_property("disposition", new_value);
+
+ THEN("no notification is issued")
+ {
+ REQUIRE_FALSE(was_notified);
+ }
+ }
+ }
+
+ AND_GIVEN("a signal handler has been subscribed to the active property")
+ {
+ auto was_notified = false;
+ instance.property_active().signal_changed().connect([&] { was_notified = true; });
+
+ WHEN("a new active state is set using set_active")
+ {
+ auto new_value = true;
+
+ CHECK_FALSE(instance.get_active() == new_value);
+ instance.set_active(new_value);
+
+ THEN("a notification is issued")
+ {
+ REQUIRE(was_notified);
+ }
+ }
+
+ WHEN("the same active state is set using set_active")
+ {
+ auto new_value = instance.get_active();
+
+ CHECK(instance.get_active() == new_value);
+ instance.set_active(new_value);
+
+ THEN("no notification is issued")
+ {
+ REQUIRE_FALSE(was_notified);
+ }
+ }
+
+ WHEN("a new active state is set using Glib::Object::set_property")
+ {
+ auto new_value = true;
+
+ CHECK_FALSE(instance.get_active() == new_value);
+ instance.set_property("active", new_value);
+
+ THEN("a notification is issued")
+ {
+ REQUIRE(was_notified);
+ }
+ }
+
+ WHEN("the same active state is set using Glib::Object::set_property")
+ {
+ auto new_value = instance.get_active();
+
+ CHECK(instance.get_active() == new_value);
+ instance.set_property("active", new_value);
+
+ THEN("no notification is issued")
+ {
+ REQUIRE_FALSE(was_notified);
+ }
+ }
+ }
+
+ AND_GIVEN("a signal handler has been subscribed to the defeated property")
+ {
+ auto was_notified = false;
+ instance.property_defeated().signal_changed().connect([&] { was_notified = true; });
+
+ WHEN("a new defeated state is set using set_defeated")
+ {
+ auto new_value = true;
+
+ CHECK_FALSE(instance.get_defeated() == new_value);
+ instance.set_defeated(new_value);
+
+ THEN("a notification is issued")
+ {
+ REQUIRE(was_notified);
+ }
+ }
+
+ WHEN("the same defeated state is set using set_defeated")
+ {
+ auto new_value = instance.get_defeated();
+
+ CHECK(instance.get_defeated() == new_value);
+ instance.set_defeated(new_value);
+
+ THEN("no notification is issued")
+ {
+ REQUIRE_FALSE(was_notified);
+ }
+ }
+
+ WHEN("a new defeated state is set using Glib::Object::set_property")
+ {
+ auto new_value = true;
+
+ CHECK_FALSE(instance.get_defeated() == new_value);
+ instance.set_property("defeated", new_value);
+
+ THEN("a notification is issued")
+ {
+ REQUIRE(was_notified);
+ }
+ }
+
+ WHEN("the same defeated state is set using Glib::Object::set_property")
+ {
+ auto new_value = instance.get_defeated();
+
+ CHECK(instance.get_defeated() == new_value);
+ instance.set_property("defeated", new_value);
+
+ THEN("no notification is issued")
+ {
+ REQUIRE_FALSE(was_notified);
+ }
+ }
+ }
+ }
+}
diff --git a/lib/tests/turnsmm/runtime_init.cpp b/lib/tests/turnsmm/runtime_init.cpp
new file mode 100644
index 0000000..963773f
--- /dev/null
+++ b/lib/tests/turnsmm/runtime_init.cpp
@@ -0,0 +1,25 @@
+
+#include "turnsmm/init.hpp"
+
+#include <catch2/reporters/catch_reporter_event_listener.hpp>
+#include <catch2/reporters/catch_reporter_registrars.hpp>
+
+#include <glibmm/init.h>
+
+namespace turns::core::tests
+{
+
+ struct glib_test_init : Catch::EventListenerBase
+ {
+ using Catch::EventListenerBase::EventListenerBase;
+
+ auto testRunStarting(Catch::TestRunInfo const &) -> void override
+ {
+ Glib::init();
+ Turns::init();
+ }
+ };
+
+ CATCH_REGISTER_LISTENER(glib_test_init);
+
+} // namespace turns::core::tests \ No newline at end of file