Thu, 30 Jan 2025 22:25:41 -0600
Add Purple.CreateConversationDetails.is_valid
This does some basic checking and creates standard errors for invalid settings
on Purple.CreateConversationDetails.
Testing Done:
Ran the unit tests under valgrind and called in the turtles as well.
Bugs closed: PIDGIN-18032
Reviewed at https://reviews.imfreedom.org/r/3791/
--- a/libpurple/meson.build Wed Jan 29 00:47:33 2025 -0600 +++ b/libpurple/meson.build Thu Jan 30 22:25:41 2025 -0600 @@ -252,6 +252,7 @@ 'purpleconnection.h', 'purplecontactinfo.h', 'purpleconversation.h', + 'purplecreateconversationdetails.h', 'purplefiletransfer.h', 'purpleplugininfo.h', 'purplepresence.h',
--- a/libpurple/purplecreateconversationdetails.c Wed Jan 29 00:47:33 2025 -0600 +++ b/libpurple/purplecreateconversationdetails.c Thu Jan 30 22:25:41 2025 -0600 @@ -20,6 +20,8 @@ * this library; if not, see <https://www.gnu.org/licenses/>. */ +#include <glib/gi18n-lib.h> + #include <gio/gio.h> #include "purplecreateconversationdetails.h" @@ -41,6 +43,9 @@ GListModel *participants; }; +G_DEFINE_QUARK(purple-create-conversation-details-error, + purple_create_conversation_details_error) + /****************************************************************************** * Helpers *****************************************************************************/ @@ -194,6 +199,60 @@ return details->participants; } +gboolean +purple_create_conversation_details_is_valid(PurpleCreateConversationDetails *details, + GError **error) +{ + guint n_participants = 0; + + g_return_val_if_fail(PURPLE_IS_CREATE_CONVERSATION_DETAILS(details), + FALSE); + + if(G_IS_LIST_MODEL(details->participants)) { + n_participants = g_list_model_get_n_items(details->participants); + } + + if(n_participants == 0) { + /* TRANSLATORS: This error message is to tell users when they tried to + * create a conversation but did not provide any other participants for + * the conversation. + */ + g_set_error_literal(error, + PURPLE_CREATE_CONVERSATION_DETAILS_ERROR, + PURPLE_CREATE_CONVERSATION_DETAILS_ERROR_NO_PARTICIPANTS, + _("no participants were provided")); + + return FALSE; + } + + /* max-participants with a value of 0 means unlimited, so there is nothing + * to validate in that case. + */ + if(details->max_participants > 0) { + if(n_participants > details->max_participants) { + const char *format = NULL; + + /* TRANSLATORS: This error message is to tell users that they tried to + * create a conversation but they have added too many participants to + * it. + */ + format = g_dngettext(GETTEXT_PACKAGE, + "a maximum of %u participant is allowed", + "a maximum of %u participants are allowed", + details->max_participants); + + g_set_error(error, + PURPLE_CREATE_CONVERSATION_DETAILS_ERROR, + PURPLE_CREATE_CONVERSATION_DETAILS_ERROR_TOO_MANY_PARTICIPANTS, + format, details->max_participants); + + return FALSE; + } + } + + return TRUE; +} + void purple_create_conversation_details_set_participants(PurpleCreateConversationDetails *details, GListModel *participants)
--- a/libpurple/purplecreateconversationdetails.h Wed Jan 29 00:47:33 2025 -0600 +++ b/libpurple/purplecreateconversationdetails.h Thu Jan 30 22:25:41 2025 -0600 @@ -39,6 +39,36 @@ PURPLE_AVAILABLE_IN_3_0 G_DECLARE_FINAL_TYPE(PurpleCreateConversationDetails, purple_create_conversation_details, PURPLE, CREATE_CONVERSATION_DETAILS, GObject) +#define PURPLE_CREATE_CONVERSATION_DETAILS_ERROR purple_create_conversation_details_error_quark() + +/** + * purple_create_conversation_details_error_quark: + * + * The error domain to identify errors in create conversation details. + * + * Returns: The error domain. + * + * Since: 3.0 + */ +PURPLE_AVAILABLE_IN_3_0 +GQuark purple_create_conversation_details_error_quark(void); + +/** + * PurpleCreateConversationDetailsError: + * @PURPLE_CREATE_CONVERSATION_DETAILS_ERROR_NO_PARTICIPANTS: no participants + * were set. + * @PURPLE_CREATE_CONVERSATION_DETAILS_ERROR_TOO_MANY_PARTICIPANTS: the number + * of participants is more than are allowed. + * + * Error codes returned by create conversation details validation. + * + * Since: 3.0 + */ +typedef enum { + PURPLE_CREATE_CONVERSATION_DETAILS_ERROR_NO_PARTICIPANTS PURPLE_AVAILABLE_ENUMERATOR_IN_3_0, + PURPLE_CREATE_CONVERSATION_DETAILS_ERROR_TOO_MANY_PARTICIPANTS PURPLE_AVAILABLE_ENUMERATOR_IN_3_0, +} PurpleCreateConversationDetailsError; + /** * PurpleCreateConversationDetails: * @@ -94,6 +124,26 @@ GListModel *purple_create_conversation_details_get_participants(PurpleCreateConversationDetails *details); /** + * purple_create_conversation_details_is_valid: + * @error: (nullable): a return address for an error + * + * Checks if a create conversation details is valid or not. + * + * This verifies that there is at least one participant and that the number of + * participants is not greater than + * [property@CreateConversationDetails:max-participants]. + * + * This may do additional checks in the future. + * + * Returns: true if the details are valid; otherwise false with @error possibly + * set. + * + * Since: 3.0 + */ +PURPLE_AVAILABLE_IN_3_0 +gboolean purple_create_conversation_details_is_valid(PurpleCreateConversationDetails *details, GError **error); + +/** * purple_create_conversation_details_set_participants: * @details: The instance. * @participants: (nullable) (transfer none): The new participants.
--- a/libpurple/tests/test_create_conversation_details.c Wed Jan 29 00:47:33 2025 -0600 +++ b/libpurple/tests/test_create_conversation_details.c Thu Jan 30 22:25:41 2025 -0600 @@ -70,6 +70,158 @@ g_assert_finalize_object(store); } +static void +test_purple_create_conversation_details_is_valid_null(void) { + PurpleCreateConversationDetails *details = NULL; + GError *error = NULL; + gboolean is_valid = FALSE; + + details = purple_create_conversation_details_new(1); + + is_valid = purple_create_conversation_details_is_valid(details, &error); + g_assert_error(error, PURPLE_CREATE_CONVERSATION_DETAILS_ERROR, + PURPLE_CREATE_CONVERSATION_DETAILS_ERROR_NO_PARTICIPANTS); + g_clear_error(&error); + g_assert_false(is_valid); + + g_assert_finalize_object(details); +} + +static void +test_purple_create_conversation_details_is_valid_empty(void) { + PurpleCreateConversationDetails *details = NULL; + GListStore *participants = NULL; + GError *error = NULL; + gboolean is_valid = FALSE; + + details = purple_create_conversation_details_new(1); + participants = g_list_store_new(PURPLE_TYPE_CONTACT); + purple_create_conversation_details_set_participants(details, + G_LIST_MODEL(participants)); + g_clear_object(&participants); + + is_valid = purple_create_conversation_details_is_valid(details, &error); + g_assert_error(error, PURPLE_CREATE_CONVERSATION_DETAILS_ERROR, + PURPLE_CREATE_CONVERSATION_DETAILS_ERROR_NO_PARTICIPANTS); + g_clear_error(&error); + g_assert_false(is_valid); + + g_assert_finalize_object(details); +} + + +static void +test_purple_create_conversation_details_is_valid_too_many(void) { + PurpleAccount *account = NULL; + PurpleContact *contact = NULL; + PurpleCreateConversationDetails *details = NULL; + GListStore *participants = NULL; + GError *error = NULL; + gboolean is_valid = FALSE; + + account = purple_account_new("test", "test"); + + details = purple_create_conversation_details_new(1); + participants = g_list_store_new(PURPLE_TYPE_CONTACT); + + /* We need to add 2 contacts to exceed the max-participants. */ + contact = purple_contact_new(account, NULL); + g_list_store_append(participants, contact); + g_clear_object(&contact); + + contact = purple_contact_new(account, NULL); + g_list_store_append(participants, contact); + g_clear_object(&contact); + + /* Set the participants on the details. */ + purple_create_conversation_details_set_participants(details, + G_LIST_MODEL(participants)); + g_clear_object(&participants); + + /* Check validation. */ + is_valid = purple_create_conversation_details_is_valid(details, &error); + g_assert_error(error, PURPLE_CREATE_CONVERSATION_DETAILS_ERROR, + PURPLE_CREATE_CONVERSATION_DETAILS_ERROR_TOO_MANY_PARTICIPANTS); + g_clear_error(&error); + g_assert_false(is_valid); + + g_assert_finalize_object(details); + g_assert_finalize_object(account); +} + +static void +test_purple_create_conversation_details_is_valid_limited(void) { + PurpleAccount *account = NULL; + PurpleContact *contact = NULL; + PurpleCreateConversationDetails *details = NULL; + GListStore *participants = NULL; + GError *error = NULL; + gboolean is_valid = FALSE; + + account = purple_account_new("test", "test"); + + details = purple_create_conversation_details_new(1); + participants = g_list_store_new(PURPLE_TYPE_CONTACT); + + contact = purple_contact_new(account, NULL); + g_list_store_append(participants, contact); + g_clear_object(&contact); + + /* Set the participants on the details. */ + purple_create_conversation_details_set_participants(details, + G_LIST_MODEL(participants)); + g_clear_object(&participants); + + /* Check validation. */ + is_valid = purple_create_conversation_details_is_valid(details, &error); + g_assert_no_error(error); + g_assert_true(is_valid); + + g_assert_finalize_object(details); + g_assert_finalize_object(account); +} + +static void +test_purple_create_conversation_details_is_valid_unlimited(void) { + PurpleAccount *account = NULL; + PurpleContact *contact = NULL; + PurpleCreateConversationDetails *details = NULL; + GListStore *participants = NULL; + GError *error = NULL; + gboolean is_valid = FALSE; + + account = purple_account_new("test", "test"); + + details = purple_create_conversation_details_new(0); + participants = g_list_store_new(PURPLE_TYPE_CONTACT); + + /* Add 3 additional contacts to spice things up! */ + contact = purple_contact_new(account, NULL); + g_list_store_append(participants, contact); + g_clear_object(&contact); + + contact = purple_contact_new(account, NULL); + g_list_store_append(participants, contact); + g_clear_object(&contact); + + contact = purple_contact_new(account, NULL); + g_list_store_append(participants, contact); + g_clear_object(&contact); + + /* Set the participants on the details. */ + purple_create_conversation_details_set_participants(details, + G_LIST_MODEL(participants)); + g_clear_object(&participants); + + /* Check validation. */ + is_valid = purple_create_conversation_details_is_valid(details, &error); + g_assert_no_error(error); + g_assert_true(is_valid); + + g_assert_finalize_object(details); + g_assert_finalize_object(account); +} + /****************************************************************************** * Main *****************************************************************************/ @@ -83,5 +235,16 @@ g_test_add_func("/create-conversation-details/properties", test_purple_create_conversation_details_properties); + g_test_add_func("/create-conversation-details/is-valid/null", + test_purple_create_conversation_details_is_valid_null); + g_test_add_func("/create-conversation-details/is-valid/empty", + test_purple_create_conversation_details_is_valid_empty); + g_test_add_func("/create-conversation-details/is-valid/too-many", + test_purple_create_conversation_details_is_valid_too_many); + g_test_add_func("/create-conversation-details/is-valid/limited", + test_purple_create_conversation_details_is_valid_limited); + g_test_add_func("/create-conversation-details/is-valid/unlimited", + test_purple_create_conversation_details_is_valid_unlimited); + return g_test_run(); }