/* * SPDX-FileCopyrightText: 2025 Felix Morgner * SPDX-License-Identifier: LGPL-2.1-only */ #include "turns-turn-order.h" #include "turns-participant.h" #include #include #include #include #include #include namespace { auto list_model_notification = std::optional>{}; auto on_list_model_notification(GListModel *, guint position, guint removed, guint added, void *) -> void { list_model_notification = std::tuple{position, removed, added}; } auto empty_notification = std::optional{}; auto on_empty_notification(TurnsTurnOrder const * instance, GParamSpec *, void *) -> void { empty_notification = turns_turn_order_get_empty(instance); } } // namespace SCENARIO("Creating a turn order", "[lib][object][lifetime]") { GIVEN("A turn order constructed using turns_turn_order_new()") { g_autoptr(TurnsTurnOrder) instance = turns_turn_order_new(); THEN("its participant count is 0") { REQUIRE(turns_turn_order_get_participant_count(instance) == 0uz); auto property_value = decltype(turns_turn_order_get_participant_count(instance)){}; g_object_get(instance, "participant-count", &property_value, nullptr); REQUIRE(property_value == 0uz); } THEN("its running state is false") { REQUIRE_FALSE(turns_turn_order_get_running(instance)); auto property_value = decltype(turns_turn_order_get_participant_count(instance)){}; g_object_get(instance, "running", &property_value, nullptr); REQUIRE_FALSE(property_value); } THEN("its item count is 0") { REQUIRE(g_list_model_get_n_items(G_LIST_MODEL(instance)) == 0); } THEN("its item type is Turns.Participant") { REQUIRE(g_list_model_get_item_type(G_LIST_MODEL(instance)) == turns_participant_get_type()); } THEN("its first item is NULL") { REQUIRE(g_list_model_get_item(G_LIST_MODEL(instance), 0) == nullptr); REQUIRE(g_list_model_get_object(G_LIST_MODEL(instance), 0) == nullptr); } THEN("its round progress is 0") { REQUIRE(turns_turn_order_get_round_progress(instance) == 0); } THEN("it's empty") { REQUIRE(turns_turn_order_get_empty(instance)); auto property_value = decltype(turns_turn_order_get_empty(instance)){}; g_object_get(instance, "empty", &property_value, nullptr); REQUIRE(property_value); } } } SCENARIO("Modifying a turn order", "[lib][object][data]") { GIVEN("An empty turn order") { g_autoptr(TurnsTurnOrder) instance = turns_turn_order_new(); CHECK(turns_turn_order_get_participant_count(instance) == 0); g_signal_connect(instance, "items-changed", reinterpret_cast(on_list_model_notification), nullptr); g_signal_connect(instance, "notify::empty", reinterpret_cast(on_empty_notification), nullptr); WHEN("a participant is added") { g_autoptr(TurnsParticipant) participant = turns_participant_new_with("Test Participant", 10.0f, TURNS_PARTICIPANT_DISPOSITION_FRIENDLY); empty_notification.reset(); turns_turn_order_add(instance, participant); THEN("its participant count is 1") { REQUIRE(turns_turn_order_get_participant_count(instance) == 1uz); auto property_value = decltype(turns_turn_order_get_participant_count(instance)){}; g_object_get(instance, "participant-count", &property_value, nullptr); REQUIRE(property_value == 1uz); } THEN("its running state is false") { REQUIRE_FALSE(turns_turn_order_get_running(instance)); } THEN("its item count is 1") { REQUIRE(g_list_model_get_n_items(G_LIST_MODEL(instance)) == 1); } THEN("its first item is the same participant") { g_autoptr(TurnsParticipant) item = TURNS_PARTICIPANT(g_list_model_get_item(G_LIST_MODEL(instance), 0)); REQUIRE(item == participant); g_autoptr(TurnsParticipant) object = TURNS_PARTICIPANT(g_list_model_get_object(G_LIST_MODEL(instance), 0)); REQUIRE(TURNS_PARTICIPANT(object) == participant); } THEN("its round progress is 0") { REQUIRE(turns_turn_order_get_round_progress(instance) == 0); } THEN("it's not empty") { REQUIRE_FALSE(turns_turn_order_get_empty(instance)); auto property_value = decltype(turns_turn_order_get_empty(instance)){}; g_object_get(instance, "empty", &property_value, nullptr); REQUIRE_FALSE(property_value); } THEN("the empty property is notified") { REQUIRE(empty_notification.has_value()); } AND_WHEN("calling clear") { list_model_notification.reset(); empty_notification.reset(); turns_turn_order_clear(instance); THEN("its participant count is 0") { REQUIRE(turns_turn_order_get_participant_count(instance) == 0); auto property_value = decltype(turns_turn_order_get_participant_count(instance)){}; g_object_get(instance, "participant-count", &property_value, nullptr); REQUIRE(property_value == 0uz); } THEN("its running state is false") { REQUIRE_FALSE(turns_turn_order_get_running(instance)); } THEN("its item count is 0") { REQUIRE(g_list_model_get_n_items(G_LIST_MODEL(instance)) == 0); } THEN("its first item is NULL") { REQUIRE(g_list_model_get_item(G_LIST_MODEL(instance), 0) == nullptr); REQUIRE(g_list_model_get_object(G_LIST_MODEL(instance), 0) == nullptr); } THEN("the items-changed notification is emitted") { REQUIRE(list_model_notification.has_value()); } THEN("all items got deleted at position 0 and none were added") { auto [position, removed, added] = *list_model_notification; REQUIRE(position == 0); REQUIRE(removed == 1); REQUIRE(added == 0); } THEN("its round progress is 0") { REQUIRE(turns_turn_order_get_round_progress(instance) == 0); } THEN("it's empty") { REQUIRE(turns_turn_order_get_empty(instance)); auto property_value = decltype(turns_turn_order_get_empty(instance)){}; g_object_get(instance, "empty", &property_value, nullptr); REQUIRE(property_value); } THEN("the empty property is notified") { REQUIRE(empty_notification.has_value()); } } AND_WHEN("removing the first element") { list_model_notification.reset(); empty_notification.reset(); turns_turn_order_remove_at(instance, 0); THEN("its participant count is 0") { REQUIRE(turns_turn_order_get_participant_count(instance) == 0); auto property_value = decltype(turns_turn_order_get_participant_count(instance)){}; g_object_get(instance, "participant-count", &property_value, nullptr); REQUIRE(property_value == 0uz); } THEN("the items-changed notification is emitted") { REQUIRE(list_model_notification.has_value()); } THEN("1 item got deleted at position 0 and none were added") { auto [position, removed, added] = *list_model_notification; REQUIRE(position == 0); REQUIRE(removed == 1); REQUIRE(added == 0); } THEN("its round progress is 0") { REQUIRE(turns_turn_order_get_round_progress(instance) == 0); } THEN("it's empty") { REQUIRE(turns_turn_order_get_empty(instance)); auto property_value = decltype(turns_turn_order_get_empty(instance)){}; g_object_get(instance, "empty", &property_value, nullptr); REQUIRE(property_value); } THEN("the empty property is notified") { REQUIRE(empty_notification.has_value()); } AND_WHEN("removing the first element again") { list_model_notification.reset(); empty_notification.reset(); turns_turn_order_remove_at(instance, 0); THEN("its participant count is 0") { REQUIRE(turns_turn_order_get_participant_count(instance) == 0); auto property_value = decltype(turns_turn_order_get_participant_count(instance)){}; g_object_get(instance, "participant-count", &property_value, nullptr); REQUIRE(property_value == 0uz); } THEN("the items-changed notification is not emitted") { REQUIRE(!list_model_notification.has_value()); } THEN("its round progress is 0") { REQUIRE(turns_turn_order_get_round_progress(instance) == 0); } THEN("it's empty") { REQUIRE(turns_turn_order_get_empty(instance)); auto property_value = decltype(turns_turn_order_get_empty(instance)){}; g_object_get(instance, "empty", &property_value, nullptr); REQUIRE(property_value); } THEN("the empty property is not notified") { REQUIRE_FALSE(empty_notification.has_value()); } } } } } } SCENARIO("Sorting a turn order") { GIVEN("A turn order with two participants - A/10/H and B/20/N") { g_autoptr(TurnsTurnOrder) instance = turns_turn_order_new(); g_autoptr(TurnsParticipant) participant_a = turns_participant_new_with("A", 10, TURNS_PARTICIPANT_DISPOSITION_HOSTILE); g_autoptr(TurnsParticipant) participant_b = turns_participant_new_with("B", 20, TURNS_PARTICIPANT_DISPOSITION_NEUTRAL); turns_turn_order_add(instance, participant_a); turns_turn_order_add(instance, participant_b); THEN("B is the first and A the second element") { g_autoptr(TurnsParticipant) first = TURNS_PARTICIPANT(g_list_model_get_object(G_LIST_MODEL(instance), 0)); g_autoptr(TurnsParticipant) second = TURNS_PARTICIPANT(g_list_model_get_object(G_LIST_MODEL(instance), 1)); REQUIRE(first == participant_b); REQUIRE(second == participant_a); } WHEN("the priority of A is changed to 30") { turns_participant_set_priority(participant_a, 30); THEN("A is the first and B the second element") { g_autoptr(TurnsParticipant) first = TURNS_PARTICIPANT(g_list_model_get_object(G_LIST_MODEL(instance), 0)); g_autoptr(TurnsParticipant) second = TURNS_PARTICIPANT(g_list_model_get_object(G_LIST_MODEL(instance), 1)); REQUIRE(first == participant_a); REQUIRE(second == participant_b); } } WHEN("the priority of A is changed to 0") { turns_participant_set_priority(participant_a, 0); THEN("B is the first and A the second element") { g_autoptr(TurnsParticipant) first = TURNS_PARTICIPANT(g_list_model_get_object(G_LIST_MODEL(instance), 0)); g_autoptr(TurnsParticipant) second = TURNS_PARTICIPANT(g_list_model_get_object(G_LIST_MODEL(instance), 1)); REQUIRE(first == participant_b); REQUIRE(second == participant_a); } } WHEN("the priority of B is changed to 0") { turns_participant_set_priority(participant_b, 0); THEN("A is the first and B the second element") { g_autoptr(TurnsParticipant) first = TURNS_PARTICIPANT(g_list_model_get_object(G_LIST_MODEL(instance), 0)); g_autoptr(TurnsParticipant) second = TURNS_PARTICIPANT(g_list_model_get_object(G_LIST_MODEL(instance), 1)); REQUIRE(first == participant_a); REQUIRE(second == participant_b); } } WHEN("the sort mode is changed to ascending") { turns_turn_order_set_sort_mode(instance, TURNS_TURN_ORDER_SORT_MODE_ASCENDING); THEN("A is the first and B the second element") { g_autoptr(TurnsParticipant) first = TURNS_PARTICIPANT(g_list_model_get_object(G_LIST_MODEL(instance), 0)); g_autoptr(TurnsParticipant) second = TURNS_PARTICIPANT(g_list_model_get_object(G_LIST_MODEL(instance), 1)); REQUIRE(first == participant_a); REQUIRE(second == participant_b); } } } }