libpurple/tests/test_conversation.c

Tue, 27 Aug 2024 00:01:29 -0500

author
Gary Kramlich <grim@reaperworld.com>
date
Tue, 27 Aug 2024 00:01:29 -0500
changeset 42908
64bbb9d4bf94
parent 42901
d3ff1696a39a
child 42916
28a4480fe746
permissions
-rw-r--r--

Add a logging property to Purple.Conversation

This property is used to determine whether or not we are logging messages.
It's setup in a way to mimic the way log toggling works in Google Chat so that
can be replicated, but our users may want to ignore that in the future.

Testing Done:
Ran the conversation tests under valgrind and let the turtles handle the rest.

Reviewed at https://reviews.imfreedom.org/r/3441/

/*
 * Purple - Internet Messaging Library
 * Copyright (C) Pidgin Developers <devel@pidgin.im>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib.h>

#include <purple.h>

#include "test_ui.h"

/******************************************************************************
 * Callbacks
 *****************************************************************************/
static void
test_purple_conversation_notify_counter_cb(GObject *obj,
                                           G_GNUC_UNUSED GParamSpec *pspec,
                                           gpointer data)
{
	guint *counter = data;

	g_assert_true(PURPLE_IS_CONVERSATION(obj));

	*counter = *counter + 1;
}

/******************************************************************************
 * Tests
 *****************************************************************************/
static void
test_purple_conversation_properties(void) {
	PurpleAccount *account = NULL;
	PurpleAccount *account1 = NULL;
	PurpleAvatar *avatar = NULL;
	PurpleAvatar *avatar1 = NULL;
	PurpleContactInfo *creator = NULL;
	PurpleContactInfo *creator1 = NULL;
	PurpleContactInfo *topic_author = NULL;
	PurpleContactInfo *topic_author1 = NULL;
	PurpleConversation *conversation = NULL;
	PurpleConversationType type = PURPLE_CONVERSATION_TYPE_UNSET;
	PurpleTags *tags = NULL;
	PurpleTypingState typing_state;
	GDateTime *created_on = NULL;
	GDateTime *created_on1 = NULL;
	GDateTime *topic_updated = NULL;
	GDateTime *topic_updated1 = NULL;
	GListModel *members = NULL;
	char *alias = NULL;
	char *description = NULL;
	char *global_id = NULL;
	char *id = NULL;
	char *name = NULL;
	char *title = NULL;
	char *title_for_display = NULL;
	char *topic = NULL;
	char *user_nickname = NULL;
	gboolean age_restricted = FALSE;
	gboolean favorite = FALSE;
	gboolean needs_attention = FALSE;
	gboolean logging = FALSE;

	account = g_object_new(
		PURPLE_TYPE_ACCOUNT,
		"id", "test",
		"protocol-id", "test",
		NULL);

	avatar = g_object_new(PURPLE_TYPE_AVATAR, NULL);
	creator = purple_contact_info_new(NULL);
	created_on = g_date_time_new_now_utc();
	topic_author = purple_contact_info_new(NULL);
	topic_updated = g_date_time_new_now_utc();

	/* Use g_object_new so we can test setting properties by name. All of them
	 * call the setter methods, so by doing it this way we exercise more of the
	 * code.
	 */
	conversation = g_object_new(
		PURPLE_TYPE_CONVERSATION,
		"account", account,
		"age-restricted", TRUE,
		"alias", "talky talk",
		"avatar", avatar,
		"created-on", created_on,
		"creator", creator,
		"description", "to describe or not to describe...",
		"favorite", TRUE,
		"id", "id1",
		"logging", TRUE,
		"name", "name1",
		"needs-attention", TRUE,
		"title", "test conversation",
		"topic", "the topic...",
		"topic-author", topic_author,
		"topic-updated", topic_updated,
		"type", PURPLE_CONVERSATION_TYPE_THREAD,
		"typing-state", PURPLE_TYPING_STATE_TYPING,
		"user-nickname", "knick-knack",
		NULL);

	/* Now use g_object_get to read all of the properties. */
	g_object_get(conversation,
		"account", &account1,
		"age-restricted", &age_restricted,
		"alias", &alias,
		"avatar", &avatar1,
		"created-on", &created_on1,
		"creator", &creator1,
		"description", &description,
		"favorite", &favorite,
		"global-id", &global_id,
		"id", &id,
		"logging", &logging,
		"members", &members,
		"name", &name,
		"needs-attention", &needs_attention,
		"tags", &tags,
		"title", &title,
		"title-for-display", &title_for_display,
		"topic", &topic,
		"topic-author", &topic_author1,
		"topic-updated", &topic_updated1,
		"type", &type,
		"typing-state", &typing_state,
		"user-nickname", &user_nickname,
		NULL);

	/* Compare all the things. */
	g_assert_true(account1 == account);
	g_clear_object(&account1);

	g_assert_true(age_restricted);

	g_assert_cmpstr(alias, ==, "talky talk");
	g_clear_pointer(&alias, g_free);

	g_assert_true(avatar1 == avatar);
	g_clear_object(&avatar1);

	g_assert_nonnull(created_on1);
	g_assert_true(g_date_time_equal(created_on1, created_on));
	g_clear_pointer(&created_on1, g_date_time_unref);

	g_assert_true(creator1 == creator);
	g_clear_object(&creator1);

	g_assert_cmpstr(description, ==, "to describe or not to describe...");
	g_clear_pointer(&description, g_free);

	g_assert_true(favorite);

	g_assert_cmpstr(global_id, ==, "test-id1");
	g_clear_pointer(&global_id, g_free);

	g_assert_cmpstr(id, ==, "id1");
	g_clear_pointer(&id, g_free);

	g_assert_true(G_IS_LIST_MODEL(members));
	g_clear_object(&members);

	g_assert_true(logging);

	g_assert_cmpstr(name, ==, "name1");
	g_clear_pointer(&name, g_free);

	g_assert_true(needs_attention);

	g_assert_true(PURPLE_IS_TAGS(tags));
	g_clear_object(&tags);

	g_assert_cmpstr(title, ==, "test conversation");
	g_clear_pointer(&title, g_free);

	g_assert_cmpstr(title_for_display, ==, "talky talk");
	g_clear_pointer(&title_for_display, g_free);

	g_assert_cmpstr(topic, ==, "the topic...");
	g_clear_pointer(&topic, g_free);

	g_assert_true(topic_author == topic_author1);
	g_clear_object(&topic_author1);

	g_assert_nonnull(topic_updated1);
	g_assert_true(g_date_time_equal(topic_updated1, topic_updated));
	g_clear_pointer(&topic_updated1, g_date_time_unref);

	g_assert_cmpuint(type, ==, PURPLE_CONVERSATION_TYPE_THREAD);

	g_assert_cmpuint(typing_state, ==, PURPLE_TYPING_STATE_TYPING);

	g_assert_cmpstr(user_nickname, ==, "knick-knack");
	g_clear_pointer(&user_nickname, g_free);

	g_clear_object(&avatar);
	g_clear_object(&topic_author);
	g_clear_pointer(&topic_updated, g_date_time_unref);
	g_clear_pointer(&created_on, g_date_time_unref);
	g_clear_object(&creator);
	g_clear_object(&account);
	g_clear_object(&conversation);
}

static void
test_purple_conversation_set_topic_full(void) {
	PurpleAccount *account = NULL;
	PurpleConversation *conversation = NULL;
	PurpleContactInfo *author = NULL;
	GDateTime *updated = NULL;
	GDateTime *updated1 = NULL;
	const char *topic = "this is the topic";

	account = purple_account_new("test", "test");
	conversation = g_object_new(
		PURPLE_TYPE_CONVERSATION,
		"account", account,
		NULL);

	g_assert_true(PURPLE_IS_CONVERSATION(conversation));

	author = purple_contact_info_new(NULL);
	updated = g_date_time_new_now_utc();

	purple_conversation_set_topic_full(conversation, topic, author, updated);

	g_assert_cmpstr(purple_conversation_get_topic(conversation), ==, topic);
	g_assert_true(purple_conversation_get_topic_author(conversation) ==
	              author);

	updated1 = purple_conversation_get_topic_updated(conversation);
	g_assert_nonnull(updated1);
	g_assert_true(g_date_time_equal(updated1, updated));

	g_clear_pointer(&updated, g_date_time_unref);
	g_clear_object(&author);
	g_clear_object(&account);
	g_clear_object(&conversation);
}

static void
test_purple_conversation_title_for_display(void) {
	PurpleAccount *account = NULL;
	PurpleConversation *conversation = NULL;
	const char *title_for_display = NULL;
	guint counter = 0;

	account = purple_account_new("test", "test");
	conversation = g_object_new(
		PURPLE_TYPE_CONVERSATION,
		"account", account,
		"id", "id1",
		NULL);
	g_signal_connect(conversation, "notify::title-for-display",
	                 G_CALLBACK(test_purple_conversation_notify_counter_cb),
	                 &counter);

	/* Make sure the defaults are all as expected. */
	g_assert_cmpuint(counter, ==, 0);
	title_for_display = purple_conversation_get_title_for_display(conversation);
	g_assert_cmpstr(title_for_display, ==, "id1");

	/* Set the alias, verify that the notify was sent for name-for-display and
	 * that it returns the right value.
	 */
	purple_conversation_set_alias(conversation, "alias1");
	g_assert_cmpuint(counter, ==, 1);
	title_for_display = purple_conversation_get_title_for_display(conversation);
	g_assert_cmpstr(title_for_display, ==, "alias1");

	/* Set the title, which won't change the name for display because alias is
	 * set, but it should still emit the signal because we don't track the
	 * name-for-display in every setter.
	 */
	purple_conversation_set_title(conversation, "title1");
	g_assert_cmpuint(counter, ==, 2);
	title_for_display = purple_conversation_get_title_for_display(conversation);
	g_assert_cmpstr(title_for_display, ==, "alias1");

	/* Now set the alias to NULL which should emit the signal and
	 * title_for_display should now return title1.
	 */
	purple_conversation_set_alias(conversation, NULL);
	g_assert_cmpuint(counter, ==, 3);
	title_for_display = purple_conversation_get_title_for_display(conversation);
	g_assert_cmpstr(title_for_display, ==, "title1");

	/* Finally remove the title as well, which should emit the signal and
	 * get_title_for_display should return the id.
	 */
	purple_conversation_set_title(conversation, NULL);
	g_assert_cmpuint(counter, ==, 4);
	title_for_display = purple_conversation_get_title_for_display(conversation);
	g_assert_cmpstr(title_for_display, ==, "id1");

	/* We're done!! */
	g_assert_finalize_object(conversation);

	/* We can't use g_assert_finalize_object because accounts automatically
	 * get added to the account manager.
	 */
	g_clear_object(&account);
}

/******************************************************************************
 * "is" tests
 *****************************************************************************/
static void
test_purple_conversation_is_dm(void) {
	PurpleAccount *account = NULL;
	PurpleConversation *conversation = NULL;

	account = purple_account_new("test", "test");
	conversation = g_object_new(
		PURPLE_TYPE_CONVERSATION,
		"account", account,
		"type", PURPLE_CONVERSATION_TYPE_DM,
		NULL);

	g_assert_true(PURPLE_IS_CONVERSATION(conversation));
	g_assert_true(purple_conversation_is_dm(conversation));
	g_assert_false(purple_conversation_is_group_dm(conversation));
	g_assert_false(purple_conversation_is_channel(conversation));
	g_assert_false(purple_conversation_is_thread(conversation));

	g_assert_finalize_object(conversation);

	g_clear_object(&account);
}

static void
test_purple_conversation_is_group_dm(void) {
	PurpleAccount *account = NULL;
	PurpleConversation *conversation = NULL;

	account = purple_account_new("test", "test");
	conversation = g_object_new(
		PURPLE_TYPE_CONVERSATION,
		"account", account,
		"type", PURPLE_CONVERSATION_TYPE_GROUP_DM,
		NULL);

	g_assert_true(PURPLE_IS_CONVERSATION(conversation));
	g_assert_false(purple_conversation_is_dm(conversation));
	g_assert_true(purple_conversation_is_group_dm(conversation));
	g_assert_false(purple_conversation_is_channel(conversation));
	g_assert_false(purple_conversation_is_thread(conversation));

	g_assert_finalize_object(conversation);

	g_clear_object(&account);
}

static void
test_purple_conversation_is_channel(void) {
	PurpleAccount *account = NULL;
	PurpleConversation *conversation = NULL;

	account = purple_account_new("test", "test");
	conversation = g_object_new(
		PURPLE_TYPE_CONVERSATION,
		"account", account,
		"type", PURPLE_CONVERSATION_TYPE_CHANNEL,
		NULL);

	g_assert_true(PURPLE_IS_CONVERSATION(conversation));
	g_assert_false(purple_conversation_is_dm(conversation));
	g_assert_false(purple_conversation_is_group_dm(conversation));
	g_assert_true(purple_conversation_is_channel(conversation));
	g_assert_false(purple_conversation_is_thread(conversation));

	g_assert_finalize_object(conversation);

	g_clear_object(&account);
}

static void
test_purple_conversation_is_thread(void) {
	PurpleAccount *account = NULL;
	PurpleConversation *conversation = NULL;

	account = purple_account_new("test", "test");
	conversation = g_object_new(
		PURPLE_TYPE_CONVERSATION,
		"account", account,
		"type", PURPLE_CONVERSATION_TYPE_THREAD,
		NULL);

	g_assert_true(PURPLE_IS_CONVERSATION(conversation));
	g_assert_false(purple_conversation_is_dm(conversation));
	g_assert_false(purple_conversation_is_group_dm(conversation));
	g_assert_false(purple_conversation_is_channel(conversation));
	g_assert_true(purple_conversation_is_thread(conversation));

	g_assert_finalize_object(conversation);

	g_clear_object(&account);
}

/******************************************************************************
 * Message Tests
 *****************************************************************************/
static void
test_purple_conversation_message_write_one(void) {
	PurpleAccount *account = NULL;
	PurpleContactInfo *info = NULL;
	PurpleConversation *conversation = NULL;
	PurpleMessage *message = NULL;
	GListModel *messages = NULL;

	account = purple_account_new("test", "test");
	conversation = g_object_new(
		PURPLE_TYPE_CONVERSATION,
		"account", account,
		NULL);

	messages = purple_conversation_get_messages(conversation);
	g_assert_nonnull(messages);
	g_assert_cmpuint(g_list_model_get_n_items(messages), ==, 0);

	info = purple_account_get_contact_info(account);
	message = purple_message_new(info, "hello world!");
	purple_conversation_write_message(conversation, message);
	g_assert_cmpuint(g_list_model_get_n_items(messages), ==, 1);

	g_clear_object(&message);
	g_clear_object(&account);
	g_clear_object(&conversation);
}

/******************************************************************************
 * Signal tests
 *****************************************************************************/
static void
test_purple_conversation_present_cb(PurpleConversation *conversation,
                                    gpointer data)
{
	guint *counter = data;

	g_assert_true(PURPLE_IS_CONVERSATION(conversation));

	*counter = *counter + 1;
}

static void
test_purple_conversation_signals_present(void) {
	PurpleAccount *account = NULL;
	PurpleConversation *conversation = NULL;
	guint counter = 0;

	account = purple_account_new("test", "test");
	conversation = g_object_new(
		PURPLE_TYPE_CONVERSATION,
		"account", account,
		"type", PURPLE_CONVERSATION_TYPE_DM,
		"name", "bleh",
		NULL);

	g_signal_connect(conversation, "present",
	                 G_CALLBACK(test_purple_conversation_present_cb),
	                 &counter);

	g_assert_cmpuint(counter, ==, 0);
	purple_conversation_present(conversation);
	g_assert_cmpuint(counter, ==, 1);

	g_assert_finalize_object(conversation);
	g_clear_object(&account);
}

/******************************************************************************
 * generate_title tests
 *****************************************************************************/
static void
test_purple_conversation_generate_title_empty(void) {
	PurpleAccount *account = NULL;
	PurpleConversation *conversation = NULL;
	const char *title = NULL;

	account = purple_account_new("test", "test");
	conversation = g_object_new(
		PURPLE_TYPE_CONVERSATION,
		"account", account,
		"type", PURPLE_CONVERSATION_TYPE_DM,
		NULL);

	title = purple_conversation_get_title(conversation);
	g_assert_null(title);

	purple_conversation_set_title(conversation, "test");
	title = purple_conversation_get_title(conversation);
	g_assert_cmpstr(title, ==, "test");

	/* There are no members in this conversation, so calling generate_title
	 * doesn't change the title.
	 */
	purple_conversation_generate_title(conversation);
	title = purple_conversation_get_title(conversation);
	g_assert_cmpstr(title, ==, "test");

	g_assert_finalize_object(conversation);
	g_clear_object(&account);
}

static void
test_purple_conversation_generate_title_dm(void) {
	PurpleAccount *account = NULL;
	PurpleContact *contact = NULL;
	PurpleConversation *conversation = NULL;
	PurpleConversationMembers *members = NULL;
	const char *title = NULL;

	account = purple_account_new("test", "test");
	conversation = g_object_new(
		PURPLE_TYPE_CONVERSATION,
		"account", account,
		"type", PURPLE_CONVERSATION_TYPE_DM,
		NULL);
	members = purple_conversation_get_members(conversation);

	title = purple_conversation_get_title(conversation);
	g_assert_null(title);

	contact = purple_contact_new(account, NULL);
	purple_contact_info_set_username(PURPLE_CONTACT_INFO(contact), "Alice");
	purple_conversation_members_add_member(members,
	                                       PURPLE_CONTACT_INFO(contact), FALSE,
	                                       NULL);

	title = purple_conversation_get_title(conversation);
	g_assert_cmpstr(title, ==, "Alice");

	/* Make sure the title updates when the display name changes. */
	purple_contact_info_set_username(PURPLE_CONTACT_INFO(contact), "alice!");
	title = purple_conversation_get_title(conversation);
	g_assert_cmpstr(title, ==, "alice!");

	g_assert_finalize_object(conversation);
	g_assert_finalize_object(contact);
	g_clear_object(&account);
}

static void
test_purple_conversation_generate_title_group_dm(void) {
	PurpleAccount *account = NULL;
	PurpleContact *contact1 = NULL;
	PurpleContact *contact2 = NULL;
	PurpleContact *contact3 = NULL;
	PurpleConversation *conversation = NULL;
	PurpleConversationMembers *members = NULL;
	const char *title = NULL;

	account = purple_account_new("test", "test");
	conversation = g_object_new(
		PURPLE_TYPE_CONVERSATION,
		"account", account,
		"type", PURPLE_CONVERSATION_TYPE_GROUP_DM,
		NULL);
	members = purple_conversation_get_members(conversation);

	title = purple_conversation_get_title(conversation);
	g_assert_null(title);

	contact1 = purple_contact_new(account, NULL);
	purple_contact_info_set_username(PURPLE_CONTACT_INFO(contact1), "Alice");
	purple_conversation_members_add_member(members,
	                                       PURPLE_CONTACT_INFO(contact1),
	                                       FALSE, NULL);

	contact2 = purple_contact_new(account, NULL);
	purple_contact_info_set_username(PURPLE_CONTACT_INFO(contact2), "Bob");
	purple_conversation_members_add_member(members,
	                                       PURPLE_CONTACT_INFO(contact2),
	                                       FALSE, NULL);

	contact3 = purple_contact_new(account, NULL);
	purple_contact_info_set_username(PURPLE_CONTACT_INFO(contact3), "Eve");
	purple_conversation_members_add_member(members,
	                                       PURPLE_CONTACT_INFO(contact3),
	                                       FALSE, NULL);

	title = purple_conversation_get_title(conversation);
	g_assert_cmpstr(title, ==, "Alice, Bob, Eve");

	/* Change some names around and verify the title was generated properly. */
	purple_contact_info_set_username(PURPLE_CONTACT_INFO(contact2), "Robert");
	purple_contact_info_set_username(PURPLE_CONTACT_INFO(contact3), "Evelyn");

	title = purple_conversation_get_title(conversation);
	g_assert_cmpstr(title, ==, "Alice, Robert, Evelyn");

	g_assert_finalize_object(conversation);
	g_assert_finalize_object(contact1);
	g_assert_finalize_object(contact2);
	g_assert_finalize_object(contact3);
	g_clear_object(&account);
}

/******************************************************************************
 * Main
 *****************************************************************************/
gint
main(gint argc, gchar *argv[]) {
	gint ret = 0;

	g_test_init(&argc, &argv, NULL);

	test_ui_purple_init();

	g_test_add_func("/conversation/properties",
	                test_purple_conversation_properties);
	g_test_add_func("/conversation/set-topic-full",
	                test_purple_conversation_set_topic_full);
	g_test_add_func("/conversation/title-for-display",
	                test_purple_conversation_title_for_display);

	g_test_add_func("/conversation/is-dm", test_purple_conversation_is_dm);
	g_test_add_func("/conversation/is-group-dm",
	                test_purple_conversation_is_group_dm);
	g_test_add_func("/conversation/is-channel",
	                test_purple_conversation_is_channel);
	g_test_add_func("/conversation/is-thread",
	                test_purple_conversation_is_thread);

	g_test_add_func("/conversation/message/write-one",
	                test_purple_conversation_message_write_one);

	g_test_add_func("/conversation/signals/present",
	                test_purple_conversation_signals_present);

	g_test_add_func("/conversation/generate-title/empty",
	                test_purple_conversation_generate_title_empty);
	g_test_add_func("/conversation/generate-title/dm",
	                test_purple_conversation_generate_title_dm);
	g_test_add_func("/conversation/generate-title/group-dm",
	                test_purple_conversation_generate_title_group_dm);

	ret = g_test_run();

	test_ui_purple_uninit();

	return ret;
}

mercurial