Automatically generate titles for DMs and GroupDMs

Wed, 08 May 2024 03:37:46 -0500

author
Gary Kramlich <grim@reaperworld.com>
date
Wed, 08 May 2024 03:37:46 -0500
changeset 42760
0c293ecd56c2
parent 42759
530549d9d6aa
child 42761
eda54ed056bd

Automatically generate titles for DMs and GroupDMs

This is one of the steps in removing PurpleConversation:name. This required a
but of tinkering with property flags and freezing the property notification,
but I think it's all good.

Testing Done:
Ran the turtles, ran the tests under valgrind, and opened conversations with the demo protocol plugin and sent some messages.

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

ChangeLog.API file | annotate | diff | comparison | revisions
libpurple/purpleconversation.c file | annotate | diff | comparison | revisions
libpurple/purpleconversation.h file | annotate | diff | comparison | revisions
libpurple/tests/test_conversation.c file | annotate | diff | comparison | revisions
libpurple/tests/test_conversation_manager.c file | annotate | diff | comparison | revisions
libpurple/tests/test_protocol_conversation.c file | annotate | diff | comparison | revisions
--- a/ChangeLog.API	Tue May 07 23:06:04 2024 -0500
+++ b/ChangeLog.API	Wed May 08 03:37:46 2024 -0500
@@ -503,6 +503,7 @@
 		* PurpleConversationType
 		* purple_conversation::conversation-switched signal
 		* purple_conversation_add_smiley
+		* purple_conversation_autoset_title
 		* purple_conversation_clear_message_history
 		* purple_conversation_close_logs
 		* purple_conversation_do_command
--- a/libpurple/purpleconversation.c	Tue May 07 23:06:04 2024 -0500
+++ b/libpurple/purpleconversation.c	Wed May 08 03:37:46 2024 -0500
@@ -47,6 +47,7 @@
 	PurpleAvatar *avatar;
 	char *name;
 	char *title;
+	gboolean title_generated;
 
 	PurpleConnectionFlags features;
 
@@ -75,6 +76,7 @@
 	PROP_AVATAR,
 	PROP_NAME,
 	PROP_TITLE,
+	PROP_TITLE_GENERATED,
 	PROP_FEATURES,
 	PROP_AGE_RESTRICTED,
 	PROP_DESCRIPTION,
@@ -112,6 +114,41 @@
  * Helpers
  **************************************************************************/
 static void
+purple_conversation_set_title_generated(PurpleConversation *conversation,
+                                        gboolean title_generated)
+{
+	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
+
+	/* If conversation isn't a dm or group dm, and title_generated is being set
+	 * to %TRUE exit immediately because generating the title is only allowed
+	 * on DMs and GroupDMs.
+	 */
+	if(conversation->type != PURPLE_CONVERSATION_TYPE_DM &&
+	   conversation->type != PURPLE_CONVERSATION_TYPE_GROUP_DM &&
+	   title_generated)
+	{
+		return;
+	}
+
+	if(conversation->title_generated != title_generated) {
+		GObject *obj = G_OBJECT(conversation);
+
+		conversation->title_generated = title_generated;
+
+		g_object_freeze_notify(obj);
+
+		if(conversation->title_generated) {
+			purple_conversation_generate_title(conversation);
+		}
+
+		g_object_notify_by_pspec(G_OBJECT(conversation),
+		                         properties[PROP_TITLE_GENERATED]);
+
+		g_object_thaw_notify(obj);
+	}
+}
+
+static void
 purple_conversation_set_id(PurpleConversation *conversation, const char *id) {
 	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
 
@@ -157,6 +194,20 @@
 }
 
 static void
+purple_conversation_set_conversation_type(PurpleConversation *conversation,
+                                          PurpleConversationType type)
+{
+	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
+
+	if(type != conversation->type) {
+		conversation->type = type;
+
+		g_object_notify_by_pspec(G_OBJECT(conversation),
+		                         properties[PROP_TYPE]);
+	}
+}
+
+static void
 purple_conversation_set_federated(PurpleConversation *conversation,
                                   gboolean federated)
 {
@@ -353,6 +404,18 @@
  * Callbacks
  **************************************************************************/
 static void
+purple_conversation_member_name_changed_cb(G_GNUC_UNUSED GObject *source,
+                                           G_GNUC_UNUSED GParamSpec *pspec,
+                                           gpointer data)
+{
+	PurpleConversation *conversation = data;
+
+	if(purple_conversation_get_title_generated(conversation)) {
+		purple_conversation_generate_title(conversation);
+	}
+}
+
+static void
 purple_conversation_account_connected_cb(GObject *obj,
                                          G_GNUC_UNUSED GParamSpec *pspec,
                                          gpointer data)
@@ -485,6 +548,10 @@
 	case PROP_TITLE:
 		g_value_set_string(value, purple_conversation_get_title(conversation));
 		break;
+	case PROP_TITLE_GENERATED:
+		g_value_set_boolean(value,
+		                    purple_conversation_get_title_generated(conversation));
+		break;
 	case PROP_FEATURES:
 		g_value_set_flags(value,
 		                  purple_conversation_get_features(conversation));
@@ -564,6 +631,17 @@
 
 	G_OBJECT_CLASS(purple_conversation_parent_class)->constructed(object);
 
+	if(purple_strempty(conversation->title)) {
+		if(conversation->type == PURPLE_CONVERSATION_TYPE_DM ||
+		   conversation->type == PURPLE_CONVERSATION_TYPE_GROUP_DM)
+		{
+			/* There's no way to add members during construction, so just call
+			 * set_title_generated.
+			 */
+			purple_conversation_set_title_generated(conversation, TRUE);
+		}
+	}
+
 	g_object_get(object, "account", &account, NULL);
 	gc = purple_account_get_connection(account);
 
@@ -575,9 +653,6 @@
 		                                 purple_connection_get_flags(gc));
 	}
 
-	/* Auto-set the title. */
-	purple_conversation_autoset_title(conversation);
-
 	g_object_unref(account);
 }
 
@@ -648,7 +723,7 @@
 		"The type of the conversation.",
 		PURPLE_TYPE_CONVERSATION_TYPE,
 		PURPLE_CONVERSATION_TYPE_UNSET,
-		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * PurpleConversation:account:
@@ -690,7 +765,7 @@
 		"name", "Name",
 		"The name of the conversation.",
 		NULL,
-		G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * PurpleConversation:title:
@@ -703,7 +778,27 @@
 		"title", "Title",
 		"The title of the conversation.",
 		NULL,
-		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+		G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleConversation:title-generated:
+	 *
+	 * Whether or not the title of the conversation was generated by
+	 * [method@Conversation.generate_title].
+	 *
+	 * Note: This only works on DMs and GroupDMs.
+	 *
+	 * If this is %TRUE, [method@Conversation.generate_title] will
+	 * automatically be called whenever a member is added or removed, or when
+	 * their display name changes.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_TITLE_GENERATED] = g_param_spec_boolean(
+		"title-generated", "title-generated",
+		"Whether or not the current title was generated.",
+		FALSE,
+		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * PurpleConversation:features:
@@ -1088,20 +1183,6 @@
 	return conversation->type;
 }
 
-void
-purple_conversation_set_conversation_type(PurpleConversation *conversation,
-                                          PurpleConversationType type)
-{
-	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
-
-	if(type != conversation->type) {
-		conversation->type = type;
-
-		g_object_notify_by_pspec(G_OBJECT(conversation),
-		                         properties[PROP_TYPE]);
-	}
-}
-
 PurpleAccount *
 purple_conversation_get_account(PurpleConversation *conversation) {
 	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), NULL);
@@ -1129,14 +1210,24 @@
                               const char *title)
 {
 	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
-	g_return_if_fail(title != NULL);
 
 	if(!purple_strequal(conversation->title, title)) {
+		GObject *obj = G_OBJECT(conversation);
+
 		g_free(conversation->title);
 		conversation->title = g_strdup(title);
 
-		g_object_notify_by_pspec(G_OBJECT(conversation),
-		                         properties[PROP_TITLE]);
+		/* We have to g_object_freeze_notify here because we're modifying more
+		 * than one property. However, purple_conversation_generate_title will
+		 * also have called g_object_freeze_notify before calling us because it
+		 * needs to set the title-generated property to TRUE even though we set
+		 * it to FALSE here. We do this, because we didn't want to write
+		 * additional API that skips that part.
+		 */
+		g_object_freeze_notify(obj);
+		g_object_notify_by_pspec(obj, properties[PROP_TITLE]);
+		purple_conversation_set_title_generated(conversation, FALSE);
+		g_object_thaw_notify(obj);
 	}
 }
 
@@ -1148,14 +1239,80 @@
 }
 
 void
-purple_conversation_autoset_title(PurpleConversation *conversation) {
-	const char *name = NULL;
+purple_conversation_generate_title(PurpleConversation *conversation) {
+	PurpleAccount *account = NULL;
+	PurpleContactInfo *account_info = NULL;
+	GString *str = NULL;
+	guint n_members = 0;
+	gboolean first = TRUE;
 
 	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
 
-	name = purple_conversation_get_name(conversation);
+	if(conversation->type != PURPLE_CONVERSATION_TYPE_DM &&
+	   conversation->type != PURPLE_CONVERSATION_TYPE_GROUP_DM)
+	{
+		g_warning("purple_conversation_generate_title called for non DM/Group "
+		          "DM conversation");
+
+		return;
+	}
+
+	account = purple_conversation_get_account(conversation);
+	account_info = PURPLE_CONTACT_INFO(account);
+
+	str = g_string_new("");
+
+	n_members = g_list_model_get_n_items(G_LIST_MODEL(conversation->members));
+	for(guint i = 0; i < n_members; i++) {
+		PurpleContactInfo *info = NULL;
+		PurpleConversationMember *member = NULL;
+		const char *name = NULL;
+
+		member = g_list_model_get_item(G_LIST_MODEL(conversation->members), i);
+		info = purple_conversation_member_get_contact_info(member);
+		if(purple_contact_info_compare(info, account_info) == 0) {
+			g_clear_object(&member);
+
+			continue;
+		}
+
+		name = purple_contact_info_get_name_for_display(info);
+		if(purple_strempty(name)) {
+			g_warning("contact %p has no displayable name", info);
 
-	purple_conversation_set_title(conversation, name);
+			g_clear_object(&member);
+
+			continue;
+		}
+
+		if(!first) {
+			g_string_append_printf(str, ", %s", name);
+		} else {
+			g_string_append(str, name);
+			first = FALSE;
+		}
+
+		g_clear_object(&member);
+	}
+
+	/* If we found at least 1 user to add, then we set the title. */
+	if(!first) {
+		GObject *obj = G_OBJECT(conversation);
+
+		g_object_freeze_notify(obj);
+		purple_conversation_set_title(conversation, str->str);
+		purple_conversation_set_title_generated(conversation, TRUE);
+		g_object_thaw_notify(obj);
+	}
+
+	g_string_free(str, TRUE);
+}
+
+gboolean
+purple_conversation_get_title_generated(PurpleConversation *conversation) {
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
+
+	return conversation->title_generated;
 }
 
 void
@@ -1639,6 +1796,16 @@
 	member = purple_conversation_member_new(info);
 	g_list_store_append(conversation->members, member);
 
+	/* Add a callback for notify::name-for-display on info. */
+	g_signal_connect_object(info, "notify::name-for-display",
+	                        G_CALLBACK(purple_conversation_member_name_changed_cb),
+	                        conversation, G_CONNECT_DEFAULT);
+
+	/* Update the title if necessary. */
+	if(purple_conversation_get_title_generated(conversation)) {
+		purple_conversation_generate_title(conversation);
+	}
+
 	g_signal_emit(conversation, signals[SIG_MEMBER_ADDED], 0, member, announce,
 	              message);
 
@@ -1667,6 +1834,16 @@
 
 	g_list_store_remove(conversation->members, position);
 
+	/* Remove our signal handlers for the member. */
+	g_signal_handlers_disconnect_by_func(info,
+	                                     purple_conversation_member_name_changed_cb,
+	                                     conversation);
+
+	/* Update our title if necessary. */
+	if(purple_conversation_get_title_generated(conversation)) {
+		purple_conversation_generate_title(conversation);
+	}
+
 	g_signal_emit(conversation, signals[SIG_MEMBER_REMOVED], 0, member,
 	              announce, message);
 
--- a/libpurple/purpleconversation.h	Tue May 07 23:06:04 2024 -0500
+++ b/libpurple/purpleconversation.h	Wed May 08 03:37:46 2024 -0500
@@ -203,21 +203,6 @@
 PurpleConversationType purple_conversation_get_conversation_type(PurpleConversation *conversation);
 
 /**
- * purple_conversation_set_conversation_type:
- * @conversation: The instance.
- * @type: The new type.
- *
- * Sets the type of @conversation to @type.
- *
- * > Note this only for the internal representation in libpurple and the
- * protocol will not be told to change the type.
- *
- * Since: 3.0
- */
-PURPLE_AVAILABLE_IN_3_0
-void purple_conversation_set_conversation_type(PurpleConversation *conversation, PurpleConversationType type);
-
-/**
  * purple_conversation_get_account:
  * @conversation: The conversation.
  *
@@ -272,18 +257,37 @@
 const char *purple_conversation_get_title(PurpleConversation *conversation);
 
 /**
- * purple_conversation_autoset_title:
- * @conversation: The conversation.
+ * purple_conversation_generate_title:
+ * @conversation: The instance.
+ *
+ * Sets the title for @conversation, which must be a DM or Group DM, to a comma
+ * separated string of the display names for each [class@ConversationMember].
  *
- * Automatically sets the specified conversation's title.
+ * If @conversation is not a DM or Group DM, no changes will be made.
+ *
+ * If no members are found, [property@Conversation:title] will not be changed.
+ *
+ * If the title is updated, [property@Conversation:title-generated] will be
+ * updated as well.
  *
- * This function takes OPT_IM_ALIAS_TAB into account, as well as the
- * user's alias.
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_conversation_generate_title(PurpleConversation *conversation);
+
+/**
+ * purple_conversation_get_title_generated:
+ * @conversation: The instance.
  *
- * Since: 2.0
+ * Gets whether or not the current title was automatically generated via
+ * [method@Conversation.generate_title].
+ *
+ * Returns: %TRUE if the title was automatically generated.
+ *
+ * Since: 3.0
  */
-PURPLE_AVAILABLE_IN_ALL
-void purple_conversation_autoset_title(PurpleConversation *conversation);
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_conversation_get_title_generated(PurpleConversation *conversation);
 
 /**
  * purple_conversation_set_name:
--- a/libpurple/tests/test_conversation.c	Tue May 07 23:06:04 2024 -0500
+++ b/libpurple/tests/test_conversation.c	Wed May 08 03:37:46 2024 -0500
@@ -47,6 +47,7 @@
 	char *description = NULL;
 	char *id = NULL;
 	char *name = NULL;
+	char *title = NULL;
 	char *topic = NULL;
 	char *user_nickname = NULL;
 	gboolean age_restricted = FALSE;
@@ -62,9 +63,6 @@
 	/* 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.
-	 *
-	 * We don't currently test title because purple_conversation_autoset_title
-	 * makes it something we don't expect it to be.
 	 */
 	conversation = g_object_new(
 		PURPLE_TYPE_CONVERSATION,
@@ -78,6 +76,7 @@
 		"features", PURPLE_CONNECTION_FLAG_HTML,
 		"id", "id1",
 		"name", "name1",
+		"title", "test conversation",
 		"topic", "the topic...",
 		"topic-author", topic_author,
 		"topic-updated", topic_updated,
@@ -99,6 +98,7 @@
 		"members", &members,
 		"name", &name,
 		"tags", &tags,
+		"title", &title,
 		"topic", &topic,
 		"topic-author", &topic_author1,
 		"topic-updated", &topic_updated1,
@@ -141,6 +141,9 @@
 	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(topic, ==, "the topic...");
 	g_clear_pointer(&topic, g_free);
 
@@ -178,7 +181,6 @@
 	conversation = g_object_new(
 		PURPLE_TYPE_CONVERSATION,
 		"account", account,
-		"name", "this is required for some reason",
 		NULL);
 
 	g_assert_true(PURPLE_IS_CONVERSATION(conversation));
@@ -215,7 +217,6 @@
 		PURPLE_TYPE_CONVERSATION,
 		"account", account,
 		"type", PURPLE_CONVERSATION_TYPE_DM,
-		"name", "this is required for some reason",
 		NULL);
 
 	g_assert_true(PURPLE_IS_CONVERSATION(conversation));
@@ -239,7 +240,6 @@
 		PURPLE_TYPE_CONVERSATION,
 		"account", account,
 		"type", PURPLE_CONVERSATION_TYPE_GROUP_DM,
-		"name", "this is required for some reason",
 		NULL);
 
 	g_assert_true(PURPLE_IS_CONVERSATION(conversation));
@@ -263,7 +263,6 @@
 		PURPLE_TYPE_CONVERSATION,
 		"account", account,
 		"type", PURPLE_CONVERSATION_TYPE_CHANNEL,
-		"name", "this is required for some reason",
 		NULL);
 
 	g_assert_true(PURPLE_IS_CONVERSATION(conversation));
@@ -287,7 +286,6 @@
 		PURPLE_TYPE_CONVERSATION,
 		"account", account,
 		"type", PURPLE_CONVERSATION_TYPE_THREAD,
-		"name", "this is required for some reason",
 		NULL);
 
 	g_assert_true(PURPLE_IS_CONVERSATION(conversation));
@@ -336,8 +334,10 @@
 	gint added_called = 0;
 	gint removed_called = 0;
 
-	/* Create our instances. */
-	info = purple_contact_info_new(NULL);
+	/* Create our instances. The id is just a uuid 4 to help us avoid a
+	 * g_warning.
+	 */
+	info = purple_contact_info_new("745c50ba-1189-48d9-827c-051783026c96");
 	account = purple_account_new("test", "test");
 	conversation = g_object_new(
 		PURPLE_TYPE_CONVERSATION,
@@ -427,7 +427,6 @@
 	conversation = g_object_new(
 		PURPLE_TYPE_CONVERSATION,
 		"account", account,
-		"name", "this is required",
 		NULL);
 
 	messages = purple_conversation_get_messages(conversation);
@@ -488,6 +487,125 @@
 }
 
 /******************************************************************************
+ * 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);
+
+	/* 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;
+	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);
+
+	contact = purple_contact_new(account, NULL);
+	purple_contact_info_set_username(PURPLE_CONTACT_INFO(contact), "Alice");
+	purple_conversation_add_member(conversation, 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;
+	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);
+
+	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_add_member(conversation, PURPLE_CONTACT_INFO(contact1),
+	                               FALSE, NULL);
+
+	contact2 = purple_contact_new(account, NULL);
+	purple_contact_info_set_username(PURPLE_CONTACT_INFO(contact2), "Bob");
+	purple_conversation_add_member(conversation, PURPLE_CONTACT_INFO(contact2),
+	                               FALSE, NULL);
+
+	contact3 = purple_contact_new(account, NULL);
+	purple_contact_info_set_username(PURPLE_CONTACT_INFO(contact3), "Eve");
+	purple_conversation_add_member(conversation, 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
@@ -520,6 +638,13 @@
 	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();
--- a/libpurple/tests/test_conversation_manager.c	Tue May 07 23:06:04 2024 -0500
+++ b/libpurple/tests/test_conversation_manager.c	Wed May 08 03:37:46 2024 -0500
@@ -69,7 +69,6 @@
 	conversation = g_object_new(
 		PURPLE_TYPE_CONVERSATION,
 		"account", account,
-		"name", "purple_conversation_autoset_title sucks",
 		NULL);
 
 	/* Add the conversation to the manager. */
@@ -136,7 +135,6 @@
 	conversation = g_object_new(
 		PURPLE_TYPE_CONVERSATION,
 		"account", account,
-		"name", "kool kats",
 		NULL);
 	purple_conversation_manager_register(manager, conversation);
 
@@ -185,7 +183,6 @@
 		PURPLE_TYPE_CONVERSATION,
 		"account", account,
 		"type", PURPLE_CONVERSATION_TYPE_DM,
-		"name", "bleh",
 		NULL);
 
 	ret = purple_conversation_manager_register(manager, conversation);
@@ -238,13 +235,13 @@
 	manager = g_object_new(PURPLE_TYPE_CONVERSATION_MANAGER, NULL);
 
 	account = purple_account_new("test", "test");
-	contact = purple_contact_new(account, NULL);
+	contact = purple_contact_new(account,
+	                             "a9780f2a-eeb5-4d6b-89cb-52e5dad3973f");
 
 	conversation1 = g_object_new(
 		PURPLE_TYPE_CONVERSATION,
 		"account", account,
 		"type", PURPLE_CONVERSATION_TYPE_DM,
-		"name", "this is required for some reason",
 		NULL);
 	purple_conversation_manager_register(manager, conversation1);
 	purple_conversation_add_member(conversation1, PURPLE_CONTACT_INFO(contact),
@@ -287,7 +284,6 @@
 		PURPLE_TYPE_CONVERSATION,
 		"account", account1,
 		"type", PURPLE_CONVERSATION_TYPE_CHANNEL,
-		"name", "this is required for some reason",
 		NULL);
 	purple_conversation_manager_register(manager, conversation1);
 
@@ -298,7 +294,6 @@
 		PURPLE_TYPE_CONVERSATION,
 		"account", account1,
 		"type", PURPLE_CONVERSATION_TYPE_DM,
-		"name", "this is required for some reason",
 		NULL);
 	purple_conversation_manager_register(manager, conversation2);
 
@@ -307,7 +302,6 @@
 		PURPLE_TYPE_CONVERSATION,
 		"account", account2,
 		"type", PURPLE_CONVERSATION_TYPE_CHANNEL,
-		"name", "this is required for some reason",
 		NULL);
 	purple_conversation_manager_register(manager, conversation2);
 
--- a/libpurple/tests/test_protocol_conversation.c	Tue May 07 23:06:04 2024 -0500
+++ b/libpurple/tests/test_protocol_conversation.c	Wed May 08 03:37:46 2024 -0500
@@ -203,7 +203,6 @@
 		conversation = g_object_new(
 			PURPLE_TYPE_CONVERSATION,
 			"account", account,
-			"name", "this is required at the moment",
 			"type", PURPLE_CONVERSATION_TYPE_DM,
 			NULL);
 		message = g_object_new(PURPLE_TYPE_MESSAGE, NULL);
@@ -262,7 +261,6 @@
 		conversation = g_object_new(
 			PURPLE_TYPE_CONVERSATION,
 			"account", account,
-			"name", "this is required at the moment",
 			"type", PURPLE_CONVERSATION_TYPE_DM,
 			NULL);
 
@@ -395,7 +393,6 @@
 		conversation = g_object_new(
 			PURPLE_TYPE_CONVERSATION,
 			"account", account,
-			"name", "this is required at the moment",
 			"type", PURPLE_CONVERSATION_TYPE_DM,
 			NULL);
 
@@ -451,7 +448,6 @@
 		conversation = g_object_new(
 			PURPLE_TYPE_CONVERSATION,
 			"account", account,
-			"name", "this is required at the moment",
 			"type", PURPLE_CONVERSATION_TYPE_DM,
 			NULL);
 
@@ -545,7 +541,6 @@
 		conversation = g_object_new(
 			PURPLE_TYPE_CONVERSATION,
 			"account", account,
-			"name", "test",
 			NULL);
 		g_task_return_pointer(task, conversation, g_object_unref);
 	}

mercurial