summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@gmail.com>2024-07-17 15:59:41 +0200
committerFelix Morgner <felix.morgner@gmail.com>2024-07-17 15:59:41 +0200
commit79c06fc454b91bc14b4a85dd6ec0c870c8ab6da8 (patch)
treebe40741f637981b1e07b620c77cb2b34c67fc8c3
parent631a8065aa9753c27f84c08bee49334c1b473bdc (diff)
downloadturns-79c06fc454b91bc14b4a85dd6ec0c870c8ab6da8.tar.xz
turns-79c06fc454b91bc14b4a85dd6ec0c870c8ab6da8.zip
domain/tests: improve turn_order tests
-rw-r--r--domain/include/turns/domain/turn_order.hpp35
-rw-r--r--domain/src/turn_order.cpp25
-rw-r--r--domain/tests/turn_order.cpp477
3 files changed, 483 insertions, 54 deletions
diff --git a/domain/include/turns/domain/turn_order.hpp b/domain/include/turns/domain/turn_order.hpp
index 59438d6..3b42562 100644
--- a/domain/include/turns/domain/turn_order.hpp
+++ b/domain/include/turns/domain/turn_order.hpp
@@ -4,6 +4,8 @@
#include "turns/domain/disposition.hpp"
#include "turns/domain/participant.hpp"
+#include <limits>
+
#include <giomm/liststore.h>
#include <glibmm/property.h>
#include <glibmm/refptr.h>
@@ -21,6 +23,8 @@ namespace turns::domain
using running_type = bool;
using round_type = unsigned int;
+ auto static constexpr invalid_participant_index = std::numeric_limits<active_participant_type>::max();
+
auto static create() -> Glib::RefPtr<turn_order>;
turn_order();
@@ -38,12 +42,43 @@ namespace turns::domain
/** Querries */
+ /**
+ * Get the index of the currently active participant of this turn order, if any.
+ *
+ * @returns an unsigned integer in the range [0, size()) if there is an active participant, or turn_order::invalid_participant_index otherwise.
+ */
auto active_participant() const noexcept -> active_participant_type;
+
+ /**
+ * Check if this turn order is empty.
+ */
auto empty() const noexcept -> empty_type;
+
+ /**
+ * Get the actor at the specified position in this turn order.
+ *
+ * @return a valid pointer to a participant object if the index was valid, nullptr otherwise.
+ */
auto get(unsigned int index) const noexcept -> Glib::RefPtr<participant>;
+
+ /**
+ * Get the underlying list model, to be used with list views.
+ */
auto list_model() -> Glib::RefPtr<Gio::ListModel>;
+
+ /**
+ * Get the current round.
+ */
auto round() const noexcept -> round_type;
+
+ /**
+ * Check if this turn order is currently running.
+ */
auto running() const noexcept -> running_type;
+
+ /**
+ * Get the size of this turn order
+ */
auto size() const noexcept -> unsigned int;
/** Properties */
diff --git a/domain/src/turn_order.cpp b/domain/src/turn_order.cpp
index 595b55d..61ccdca 100644
--- a/domain/src/turn_order.cpp
+++ b/domain/src/turn_order.cpp
@@ -40,7 +40,7 @@ namespace turns::domain
turn_order::turn_order()
: Glib::ObjectBase{typeid(turn_order)}
, m_model{Gio::ListStore<participant>::create()}
- , m_active_participant(*this, "active_participant", std::numeric_limits<active_participant_type>::max())
+ , m_active_participant(*this, "active_participant", invalid_participant_index)
, m_empty{*this, "empty", true}
, m_has_next{*this, "has-next", false}
, m_has_previous{*this, "has-previous", false}
@@ -62,7 +62,7 @@ namespace turns::domain
auto position = m_model->insert_sorted(participant, comparator);
participant->property_priority().signal_changed().connect([this] { m_model->sort(comparator); });
- if (m_active_participant != std::numeric_limits<active_participant_type>::max() && position <= m_active_participant)
+ if (m_active_participant != invalid_participant_index && position <= m_active_participant)
{
m_active_participant = m_active_participant + 1;
}
@@ -72,7 +72,7 @@ namespace turns::domain
auto turn_order::clear() -> void
{
m_model->remove_all();
- m_active_participant = std::numeric_limits<active_participant_type>::max();
+ m_active_participant = invalid_participant_index;
m_has_next = false;
m_has_previous = false;
m_running = false;
@@ -113,18 +113,21 @@ namespace turns::domain
m_model->remove(index);
if (empty())
{
- m_active_participant = std::numeric_limits<active_participant_type>::max();
+ m_active_participant = invalid_participant_index;
m_has_next = false;
m_has_previous = false;
m_running = false;
}
- else if (m_active_participant >= size() - 1)
+ else if (m_active_participant != invalid_participant_index)
{
- m_active_participant = size() - 1;
- }
- else if (index <= m_active_participant)
- {
- m_active_participant = m_active_participant - 1;
+ if (m_active_participant > size() - 1)
+ {
+ m_active_participant = size() - 1;
+ }
+ else if (index <= m_active_participant)
+ {
+ m_active_participant = m_active_participant - 1;
+ }
}
}
@@ -136,7 +139,7 @@ namespace turns::domain
auto turn_order::start() -> void
{
- if (m_active_participant == std::numeric_limits<active_participant_type>::max())
+ if (m_active_participant == invalid_participant_index)
{
m_active_participant = 0;
}
diff --git a/domain/tests/turn_order.cpp b/domain/tests/turn_order.cpp
index 5bdc157..de1689c 100644
--- a/domain/tests/turn_order.cpp
+++ b/domain/tests/turn_order.cpp
@@ -1,71 +1,462 @@
#include "turns/domain/turn_order.hpp"
-#include "turns/domain/participant.hpp"
-
#include <catch2/catch_test_macros.hpp>
-#include <compare>
-
-#include <glibmm/init.h>
+#include <glibmm/ustring.h>
namespace turns::domain::tests
{
- TEST_CASE("A freshly constructed turn order")
+ SCENARIO("Queries on a fresh turn_order instance", "[turn_order]")
{
- auto instance = turn_order::create();
-
- SECTION("can be created")
+ GIVEN("an empty turn_order")
{
- REQUIRE(instance);
- }
+ auto instance = turn_order::create();
- SECTION("has 0 items")
- {
- REQUIRE(instance->size() == 0);
- }
+ THEN("active_participant() is turn_order::invalid_participant_index")
+ {
+ REQUIRE(instance->active_participant() == turn_order::invalid_participant_index);
+ }
- SECTION("accepts a new participant")
- {
- instance->add("River along the Field", 14, disposition::friendly);
- REQUIRE(instance->size() == 1);
- }
+ THEN("empty() is true")
+ {
+ REQUIRE(instance->empty());
+ }
- SECTION("does nothing when trying to remove an item if no items were added beforehand")
- {
- instance->remove(5);
- REQUIRE(instance->size() == 0);
- }
+ THEN("get() returns a nullptr")
+ {
+ REQUIRE(instance->get(0) == nullptr);
+ }
- SECTION("automatically sorts elements added in descending order of priority")
- {
- SECTION("when adding the higher one last")
+ THEN("list_model() returns a non-null pointer")
+ {
+ REQUIRE(instance->list_model());
+ }
+
+ THEN("round() returns 0")
+ {
+ REQUIRE(instance->round() == 0);
+ }
+
+ THEN("running() returns false")
+ {
+ REQUIRE_FALSE(instance->running());
+ }
+
+ THEN("size() returns 0")
+ {
+ REQUIRE(instance->size() == 0);
+ }
+
+ THEN("size() returns the same value as list_model()->get_n_item()")
+ {
+ REQUIRE(instance->size() == instance->list_model()->get_n_items());
+ }
+
+ WHEN("accessing turn_order:active-participant")
{
- instance->add("Tree Blossom", 6, disposition::friendly);
- instance->add("Fish in the River", 12, disposition::friendly);
- REQUIRE(instance->get(0)->get_name() == "Fish in the River");
+ THEN("get() returns turn_order::invalid_participant_index")
+ {
+ REQUIRE(instance->property_active_participant().get_value() == turn_order::invalid_participant_index);
+ }
+
+ THEN("get_object() returns a pointer to the instance")
+ {
+ REQUIRE(instance->property_active_participant().get_object() == instance.get());
+ }
+
+ THEN("get_name() returns \"active-participant\"")
+ {
+ REQUIRE(instance->property_active_participant().get_name() == Glib::ustring{"active-participant"});
+ }
}
- SECTION("when adding the higher one first")
+ WHEN("accessing turn_order:empty")
{
- instance->add("Fish in the River", 12, disposition::friendly);
- instance->add("Tree Blossom", 6, disposition::friendly);
- REQUIRE(instance->get(0)->get_name() == "Fish in the River");
+ THEN("get() returns true")
+ {
+ REQUIRE(instance->property_empty().get_value());
+ }
+
+ THEN("get_object() returns a pointer to the instance")
+ {
+ REQUIRE(instance->property_empty().get_object() == instance.get());
+ }
+
+ THEN("get_name() returns \"empty\"")
+ {
+ REQUIRE(instance->property_empty().get_name() == Glib::ustring{"empty"});
+ }
}
- SECTION("keeping the insertion order when adding items with equal priority")
+ WHEN("accessing turn_order:has-next")
{
- instance->add("Fish in the River", 6, disposition::friendly);
- instance->add("Tree Blossom", 6, disposition::friendly);
- REQUIRE(instance->get(0)->get_name() == "Fish in the River");
+ THEN("get() returns false")
+ {
+ REQUIRE_FALSE(instance->property_has_next().get_value());
+ }
+
+ THEN("get_object() returns a pointer to the instance")
+ {
+ REQUIRE(instance->property_has_next().get_object() == instance.get());
+ }
+
+ THEN("get_name() returns \"has-next\"")
+ {
+ REQUIRE(instance->property_has_next().get_name() == Glib::ustring{"has-next"});
+ }
+ }
+
+ WHEN("accessing turn_order:has-previous")
+ {
+ THEN("get() returns false")
+ {
+ REQUIRE_FALSE(instance->property_has_previous().get_value());
+ }
+
+ THEN("get_object() returns a pointer to the instance")
+ {
+ REQUIRE(instance->property_has_previous().get_object() == instance.get());
+ }
+
+ THEN("get_name() returns \"has-previous\"")
+ {
+ REQUIRE(instance->property_has_previous().get_name() == Glib::ustring{"has-previous"});
+ }
+ }
+
+ WHEN("accessing turn_order:running")
+ {
+ THEN("get() returns false")
+ {
+ REQUIRE_FALSE(instance->property_running().get_value());
+ }
+
+ THEN("get_object() returns a pointer to the instance")
+ {
+ REQUIRE(instance->property_running().get_object() == instance.get());
+ }
+
+ THEN("get_name() returns \"running\"")
+ {
+ REQUIRE(instance->property_running().get_name() == Glib::ustring{"running"});
+ }
+ }
+
+ WHEN("accessing turn_order:running")
+ {
+ THEN("get() returns 0")
+ {
+ REQUIRE(instance->property_round().get_value() == 0);
+ }
+
+ THEN("get_object() returns a pointer to the instance")
+ {
+ REQUIRE(instance->property_round().get_object() == instance.get());
+ }
+
+ THEN("get_name() returns \"round\"")
+ {
+ REQUIRE(instance->property_round().get_name() == Glib::ustring{"round"});
+ }
}
}
+ }
+
+ SCENARIO("Modification of an empty turn_order", "[turn_order]")
+ {
+ auto instance = turn_order::create();
- SECTION("does not accept the same item twice by components")
+ GIVEN("a single participant was added")
{
- instance->add("Frozen Apple Tree", 2.1, disposition::friendly);
- instance->add("Frozen Apple Tree", 2.1, disposition::friendly);
- REQUIRE(instance->size() == 1);
+ auto constexpr priority = 12.0f;
+ instance->add("Participant #0", priority, disposition::friendly);
+
+ AND_GIVEN("the turn_order was never started")
+ {
+ WHEN("no other modification occurs")
+ {
+ THEN("active_participant() returns turn_order::invalid_participant_index")
+ {
+ REQUIRE(instance->active_participant() == turn_order::invalid_participant_index);
+ }
+
+ THEN("empty() returns false")
+ {
+ REQUIRE_FALSE(instance->empty());
+ }
+
+ THEN("get() returns a valid pointer for index 0")
+ {
+ REQUIRE(instance->get(0));
+ }
+
+ THEN("get() returns a nullptr for an index greater than 0")
+ {
+ REQUIRE(instance->get(1) == nullptr);
+ }
+
+ THEN("round() returns 0")
+ {
+ REQUIRE(instance->round() == 0);
+ }
+
+ THEN("running() returns false")
+ {
+ REQUIRE_FALSE(instance->running());
+ }
+
+ THEN("size() returns 1")
+ {
+ REQUIRE(instance->size() == 1);
+ }
+
+ THEN("turn_order:has-next is false")
+ {
+ REQUIRE(instance->property_has_next() == false);
+ }
+
+ THEN("turn_order:has-previous is false")
+ {
+ REQUIRE(instance->property_has_previous() == false);
+ }
+
+ THEN("turn_order:running is false")
+ {
+ REQUIRE(instance->property_running() == false);
+ }
+ }
+
+ WHEN("the participant is removed again")
+ {
+ instance->remove(0);
+
+ THEN("active_participant() returns turn_order::invalid_participant_index")
+ {
+ REQUIRE(instance->active_participant() == turn_order::invalid_participant_index);
+ }
+
+ THEN("empty() returns true")
+ {
+ REQUIRE(instance->empty());
+ }
+
+ THEN("get() returns a nullptr for index 0")
+ {
+ REQUIRE(instance->get(0) == nullptr);
+ }
+
+ THEN("round() returns 0")
+ {
+ REQUIRE(instance->round() == 0);
+ }
+
+ THEN("running() returns false")
+ {
+ REQUIRE_FALSE(instance->running());
+ }
+
+ THEN("size() returns 0")
+ {
+ REQUIRE(instance->size() == 0);
+ }
+
+ THEN("turn_order:has-next is false")
+ {
+ REQUIRE(instance->property_has_next() == false);
+ }
+
+ THEN("turn_order:has-previous is false")
+ {
+ REQUIRE(instance->property_has_previous() == false);
+ }
+
+ THEN("turn_order:running is false")
+ {
+ REQUIRE(instance->property_running() == false);
+ }
+ }
+
+ WHEN("another participant with the same priority is added")
+ {
+ instance->add("Participant #1", priority, disposition::friendly);
+
+ THEN("active_participant() returns turn_order::invalid_participant_index")
+ {
+ REQUIRE(instance->active_participant() == turn_order::invalid_participant_index);
+ }
+
+ THEN("empty() returns false")
+ {
+ REQUIRE_FALSE(instance->empty());
+ }
+
+ THEN("get() returns a valid pointer for index 0")
+ {
+ REQUIRE(instance->get(0));
+ }
+
+ THEN("get() returns a valid pointer for index 1")
+ {
+ REQUIRE(instance->get(1));
+ }
+
+ THEN("get() returns a nullptr for an index greater than 1")
+ {
+ REQUIRE(instance->get(2) == nullptr);
+ }
+
+ THEN("get(0) returns the participant that was added first")
+ {
+ REQUIRE(instance->get(0)->get_name() == "Participant #0");
+ }
+
+ THEN("get(1) returns the participant that was added second")
+ {
+ REQUIRE(instance->get(1)->get_name() == "Participant #1");
+ }
+
+ THEN("round() returns 0")
+ {
+ REQUIRE(instance->round() == 0);
+ }
+
+ THEN("running() returns false")
+ {
+ REQUIRE_FALSE(instance->running());
+ }
+
+ THEN("size() returns 2")
+ {
+ REQUIRE(instance->size() == 2);
+ }
+
+ THEN("turn_order:has-next is false")
+ {
+ REQUIRE(instance->property_has_next() == false);
+ }
+
+ THEN("turn_order:has-previous is false")
+ {
+ REQUIRE(instance->property_has_previous() == false);
+ }
+
+ THEN("turn_order:running is false")
+ {
+ REQUIRE(instance->property_running() == false);
+ }
+
+ AND_WHEN("the participant at index 0 is removed")
+ {
+ instance->remove(0);
+
+ THEN("active_participant() returns turn_order::invalid_participant_index")
+ {
+ REQUIRE(instance->active_participant() == turn_order::invalid_participant_index);
+ }
+
+ THEN("empty() returns false")
+ {
+ REQUIRE_FALSE(instance->empty());
+ }
+
+ THEN("get() returns a valid pointer for index 0")
+ {
+ REQUIRE(instance->get(0));
+ }
+
+ THEN("get() returns a nullptr for an index greater than 0")
+ {
+ REQUIRE(instance->get(1) == nullptr);
+ }
+
+ THEN("get(0) returns the participant that was added second")
+ {
+ REQUIRE(instance->get(0)->get_name() == "Participant #1");
+ }
+
+ THEN("round() returns 0")
+ {
+ REQUIRE(instance->round() == 0);
+ }
+
+ THEN("running() returns false")
+ {
+ REQUIRE_FALSE(instance->running());
+ }
+
+ THEN("size() returns 1")
+ {
+ REQUIRE(instance->size() == 1);
+ }
+
+ THEN("turn_order:has-next is false")
+ {
+ REQUIRE(instance->property_has_next() == false);
+ }
+
+ THEN("turn_order:has-previous is false")
+ {
+ REQUIRE(instance->property_has_previous() == false);
+ }
+
+ THEN("turn_order:running is false")
+ {
+ REQUIRE(instance->property_running() == false);
+ }
+ }
+
+ AND_WHEN("the turn_order is cleared")
+ {
+ instance->clear();
+
+ THEN("active_participant() returns turn_order::invalid_participant_index")
+ {
+ REQUIRE(instance->active_participant() == turn_order::invalid_participant_index);
+ }
+
+ THEN("empty() returns true")
+ {
+ REQUIRE(instance->empty());
+ }
+
+ THEN("get() returns a nullptr pointer for index 0")
+ {
+ REQUIRE(instance->get(0) == nullptr);
+ }
+
+ THEN("round() returns 0")
+ {
+ REQUIRE(instance->round() == 0);
+ }
+
+ THEN("running() returns false")
+ {
+ REQUIRE_FALSE(instance->running());
+ }
+
+ THEN("size() returns 0")
+ {
+ REQUIRE(instance->size() == 0);
+ }
+
+ THEN("turn_order:has-next is false")
+ {
+ REQUIRE(instance->property_has_next() == false);
+ }
+
+ THEN("turn_order:has-previous is false")
+ {
+ REQUIRE(instance->property_has_previous() == false);
+ }
+
+ THEN("turn_order:running is false")
+ {
+ REQUIRE(instance->property_running() == false);
+ }
+ }
+ }
+ }
}
}