# HG changeset patch
# Node ID d2aa432ab723cebf65134f84647dceeb619fe1cd
# Parent  c72c9ce6317768c54232c20a3ca96b42756f3ff5
diff --git a/libpurple/purpleprotocolconversation.c b/libpurple/purpleprotocolconversation.c
--- a/libpurple/purpleprotocolconversation.c
+++ b/libpurple/purpleprotocolconversation.c
@@ -285,6 +285,26 @@
 	return FALSE;
 }
 
+gboolean
+purple_protocol_conversation_implements_set_topic(PurpleProtocolConversation *protocol)
+{
+	PurpleProtocolConversationInterface *iface = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL_CONVERSATION(protocol), FALSE);
+
+	iface = PURPLE_PROTOCOL_CONVERSATION_GET_IFACE(protocol);
+
+	if(iface->set_topic_async == NULL) {
+		return FALSE;
+	}
+
+	if(iface->set_topic_finish == NULL) {
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 void
 purple_protocol_conversation_set_topic_async(PurpleProtocolConversation *protocol,
                                              PurpleConversation *conversation,
@@ -413,6 +433,26 @@
 	return FALSE;
 }
 
+gboolean
+purple_protocol_conversation_implements_set_avatar(PurpleProtocolConversation *protocol)
+{
+	PurpleProtocolConversationInterface *iface = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL_CONVERSATION(protocol), FALSE);
+
+	iface = PURPLE_PROTOCOL_CONVERSATION_GET_IFACE(protocol);
+
+	if(iface->set_avatar_async == NULL) {
+		return FALSE;
+	}
+
+	if(iface->set_avatar_finish == NULL) {
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 void
 purple_protocol_conversation_set_avatar_async(PurpleProtocolConversation *protocol,
                                               PurpleConversation *conversation,
@@ -532,3 +572,148 @@
 		          G_OBJECT_TYPE_NAME(protocol));
 	}
 }
+
+gboolean
+purple_protocol_conversation_implements_set_title(PurpleProtocolConversation *protocol)
+{
+	PurpleProtocolConversationInterface *iface = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL_CONVERSATION(protocol), FALSE);
+
+	iface = PURPLE_PROTOCOL_CONVERSATION_GET_IFACE(protocol);
+	if(iface->set_title_async == NULL) {
+		return FALSE;
+	}
+
+	if(iface->set_title_finish == NULL) {
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void
+purple_protocol_conversation_set_title_async(PurpleProtocolConversation *protocol,
+                                             PurpleConversation *conversation,
+                                             const char *title,
+                                             GCancellable *cancellable,
+                                             GAsyncReadyCallback callback,
+                                             gpointer data)
+{
+	PurpleProtocolConversationInterface *iface = NULL;
+
+	g_return_if_fail(PURPLE_IS_PROTOCOL_CONVERSATION(protocol));
+	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
+
+	iface = PURPLE_PROTOCOL_CONVERSATION_GET_IFACE(protocol);
+	if(iface != NULL && iface->set_title_async != NULL) {
+		iface->set_title_async(protocol, conversation, title, cancellable,
+		                       callback, data);
+	} else {
+		g_task_report_new_error(G_OBJECT(protocol), callback, data,
+		                        purple_protocol_conversation_set_title_async,
+		                        PURPLE_PROTOCOL_CONVERSATION_DOMAIN, 0,
+		                        "%s does not implement set_title_async",
+		                        G_OBJECT_TYPE_NAME(protocol));
+	}
+}
+
+gboolean
+purple_protocol_conversation_set_title_finish(PurpleProtocolConversation *protocol,
+                                              GAsyncResult *result,
+                                              GError **error)
+{
+	PurpleProtocolConversationInterface *iface = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL_CONVERSATION(protocol), FALSE);
+	g_return_val_if_fail(G_IS_ASYNC_RESULT(result), FALSE);
+
+	if(g_async_result_is_tagged(result,
+	                            purple_protocol_conversation_set_title_async))
+	{
+		return g_task_propagate_boolean(G_TASK(result), error);
+	}
+
+	iface = PURPLE_PROTOCOL_CONVERSATION_GET_IFACE(protocol);
+	if(iface != NULL && iface->set_title_finish != NULL) {
+		return iface->set_title_finish(protocol, result, error);
+	}
+
+	g_warning("purple_protocol_conversation_set_title_finish called without "
+	          "calling purple_protocol_conversation_set_title_async");
+
+	return FALSE;
+}
+
+gboolean
+purple_protocol_conversation_implements_set_description(PurpleProtocolConversation *protocol)
+{
+	PurpleProtocolConversationInterface *iface = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL_CONVERSATION(protocol), FALSE);
+
+	iface = PURPLE_PROTOCOL_CONVERSATION_GET_IFACE(protocol);
+	if(iface->set_description_async == NULL) {
+		return FALSE;
+	}
+
+	if(iface->set_description_finish == NULL) {
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void
+purple_protocol_conversation_set_description_async(PurpleProtocolConversation *protocol,
+                                                   PurpleConversation *conversation,
+                                                   const char *description,
+                                                   GCancellable *cancellable,
+                                                   GAsyncReadyCallback callback,
+                                                   gpointer data)
+{
+	PurpleProtocolConversationInterface *iface = NULL;
+
+	g_return_if_fail(PURPLE_IS_PROTOCOL_CONVERSATION(protocol));
+	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
+
+	iface = PURPLE_PROTOCOL_CONVERSATION_GET_IFACE(protocol);
+	if(iface != NULL && iface->set_description_async != NULL) {
+		iface->set_description_async(protocol, conversation, description,
+		                             cancellable, callback, data);
+	} else {
+		g_task_report_new_error(G_OBJECT(protocol), callback, data,
+		                        purple_protocol_conversation_set_description_async,
+		                        PURPLE_PROTOCOL_CONVERSATION_DOMAIN, 0,
+		                        "%s does not implement set_description_async",
+		                        G_OBJECT_TYPE_NAME(protocol));
+	}
+}
+
+gboolean
+purple_protocol_conversation_set_description_finish(PurpleProtocolConversation *protocol,
+                                                    GAsyncResult *result,
+                                                    GError **error)
+{
+	PurpleProtocolConversationInterface *iface = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL_CONVERSATION(protocol), FALSE);
+	g_return_val_if_fail(G_IS_ASYNC_RESULT(result), FALSE);
+
+	if(g_async_result_is_tagged(result,
+	                            purple_protocol_conversation_set_description_async))
+	{
+		return g_task_propagate_boolean(G_TASK(result), error);
+	}
+
+	iface = PURPLE_PROTOCOL_CONVERSATION_GET_IFACE(protocol);
+	if(iface != NULL && iface->set_description_finish != NULL) {
+		return iface->set_description_finish(protocol, result, error);
+	}
+
+	g_warning("purple_protocol_conversation_set_description_finish called "
+	          "without calling "
+	          "purple_protocol_conversation_set_description_async");
+
+	return FALSE;
+}
diff --git a/libpurple/purpleprotocolconversation.h b/libpurple/purpleprotocolconversation.h
--- a/libpurple/purpleprotocolconversation.h
+++ b/libpurple/purpleprotocolconversation.h
@@ -97,6 +97,12 @@
 
 	void (*refresh)(PurpleProtocolConversation *protocol, PurpleConversation *conversation);
 
+	void (*set_title_async)(PurpleProtocolConversation *protocol, PurpleConversation *conversation, const char *title, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
+	gboolean (*set_title_finish)(PurpleProtocolConversation *protocol, GAsyncResult *result, GError **error);
+
+	void (*set_description_async)(PurpleProtocolConversation *protocol, PurpleConversation *conversation, const char *description, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
+	gboolean (*set_description_finish)(PurpleProtocolConversation *protocol, GAsyncResult *result, GError **error);
+
 	/*< private >*/
 	gpointer reserved[8];
 };
@@ -284,6 +290,23 @@
 gboolean purple_protocol_conversation_send_message_finish(PurpleProtocolConversation *protocol, GAsyncResult *result, GError **error);
 
 /**
+ * purple_protocol_conversation_implements_set_topic:
+ * @protocol: the instance
+ *
+ * Checks if a protocol implements all of the set topic virtual functions.
+ *
+ * This checks if the [vfunc@ProtocolConversation.set_topic_async] and
+ * [vfunc@ProtocolConversation.set_topic_finish] functions are implemented.
+ *
+ * Returns: true if @protocol implements all of the virtual functions for
+ *          sending messages, otherwise false.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_protocol_conversation_implements_set_topic(PurpleProtocolConversation *protocol);
+
+/**
  * purple_protocol_conversation_set_topic_async:
  * @protocol: The instance.
  * @conversation: The conversation whose topic to set.
@@ -383,6 +406,23 @@
 gboolean purple_protocol_conversation_join_channel_finish(PurpleProtocolConversation *protocol, GAsyncResult *result, GError **error);
 
 /**
+ * purple_protocol_conversation_implements_set_avatar:
+ * @protocol: the instance
+ *
+ * Checks if a protocol implements setting avatars.
+ *
+ * This verifies that @protocol implements
+ * [vfunc@ProtocolConversation.set_avatar_async] and
+ * [vfunc@ProtocolConversation.set_avatar_finish].
+ *
+ * Returns: true if everything is implemented, otherwise false.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_protocol_conversation_implements_set_avatar(PurpleProtocolConversation *protocol);
+
+/**
  * purple_protocol_conversation_set_avatar_async:
  * @protocol: The instance.
  * @conversation: The conversation instance.
@@ -478,6 +518,109 @@
 PURPLE_AVAILABLE_IN_3_0
 void purple_protocol_conversation_refresh(PurpleProtocolConversation *protocol, PurpleConversation *conversation);
 
+/**
+ * purple_protocol_conversation_implements_set_title:
+ * @protocol: The instance.
+ *
+ * Checks if @protocol implements [vfunc@ProtocolConversation.set_title_async]
+ * and [vfunc@ProtocolConversation.set_title_finish].
+ *
+ * Returns: %TRUE if everything is implemented, otherwise %FALSE.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_protocol_conversation_implements_set_title(PurpleProtocolConversation *protocol);
+
+/**
+ * purple_protocol_conversation_set_title_async:
+ * @protocol: the instance
+ * @conversation: the conversation instance
+ * @title: (nullable): the new title
+ * @cancellable: (nullable): optional GCancellable object
+ * @callback: (nullable) (scope async): callback to call after the title has
+ *            been set
+ * @data: (nullable): optional user data to pass to @callback.
+ *
+ * Sets the title of a conversation.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_protocol_conversation_set_title_async(PurpleProtocolConversation *protocol, PurpleConversation *conversation, const char *title, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
+
+/**
+ * purple_protocol_conversation_set_title_finish:
+ * @protocol: the instance
+ * @result: the [iface@Gio.AsyncResult] from the previous
+ *          [method@ProtocolConversation.set_title_async] call
+ * @error: Return address for a #GError, or %NULL
+ *
+ * Finishes a previous call to
+ * [method@ProtocolConversation.set_title_async]. This should be called from
+ * the callback of that function to get the result of whether or not the title
+ * was set successfully.
+ *
+ * Returns: true if the title was set successfully, otherwise false with
+ *          @error possibly set.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_protocol_conversation_set_title_finish(PurpleProtocolConversation *protocol, GAsyncResult *result, GError **error);
+
+/**
+ * purple_protocol_conversation_implements_set_description:
+ * @protocol: The instance.
+ *
+ * Checks if @protocol implements
+ * [vfunc@ProtocolConversation.set_description_async] and
+ * [vfunc@ProtocolConversation.set_description_finish].
+ *
+ * Returns: %TRUE if everything is implemented, otherwise %FALSE.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_protocol_conversation_implements_set_description(PurpleProtocolConversation *protocol);
+
+/**
+ * purple_protocol_conversation_set_description_async:
+ * @protocol: the instance
+ * @conversation: the conversation instance
+ * @description: (nullable): the new description
+ * @cancellable: (nullable): optional GCancellable object
+ * @callback: (nullable) (scope async): callback to call after the description
+ *            has been set
+ * @data: (nullable): optional user data to pass to @callback.
+ *
+ * Sets the description of a conversation.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_protocol_conversation_set_description_async(PurpleProtocolConversation *protocol, PurpleConversation *conversation, const char *description, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);
+
+/**
+ * purple_protocol_conversation_set_description_finish:
+ * @protocol: the instance
+ * @result: the [iface@Gio.AsyncResult] from the previous
+ *          [method@ProtocolConversation.set_description_async] call
+ * @error: Return address for a #GError, or %NULL
+ *
+ * Finishes a previous call to
+ * [method@ProtocolConversation.set_description_async]. This should be called
+ * from the callback of that function to get the result of whether or not the
+ * description was set successfully.
+ *
+ * Returns: true if the description was set successfully, otherwise false with
+ *          @error possibly set.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_protocol_conversation_set_description_finish(PurpleProtocolConversation *protocol, GAsyncResult *result, GError **error);
+
 G_END_DECLS
 
 #endif /* PURPLE_PROTOCOL_CONVERSATION_H */
diff --git a/libpurple/tests/test_protocol_conversation.c b/libpurple/tests/test_protocol_conversation.c
--- a/libpurple/tests/test_protocol_conversation.c
+++ b/libpurple/tests/test_protocol_conversation.c
@@ -283,6 +283,18 @@
 }
 
 static void
+test_purple_protocol_conversation_empty_implements_set_topic(void) {
+	PurpleProtocolConversation *protocol = NULL;
+
+	protocol = g_object_new(test_purple_protocol_conversation_empty_get_type(),
+	                        NULL);
+
+	g_assert_false(purple_protocol_conversation_implements_set_topic(protocol));
+
+	g_assert_finalize_object(protocol);
+}
+
+static void
 test_purple_protocol_conversation_empty_set_topic_async(void) {
 	PurpleAccount *account = NULL;
 	PurpleConversation *conversation = NULL;
@@ -375,6 +387,18 @@
 }
 
 static void
+test_purple_protocol_conversation_empty_implements_set_avatar(void) {
+	PurpleProtocolConversation *protocol = NULL;
+
+	protocol = g_object_new(test_purple_protocol_conversation_empty_get_type(),
+	                        NULL);
+
+	g_assert_false(purple_protocol_conversation_implements_set_avatar(protocol));
+
+	g_assert_finalize_object(protocol);
+}
+
+static void
 test_purple_protocol_conversation_empty_set_avatar_cb(GObject *source,
                                                       GAsyncResult *result,
                                                       G_GNUC_UNUSED gpointer data)
@@ -497,6 +521,118 @@
 	g_test_trap_assert_stderr("*Purple-WARNING*TestPurpleProtocolConversationEmpty*refresh*");
 }
 
+static void
+test_purple_protocol_conversation_empty_implements_set_title(void) {
+	PurpleProtocolConversation *protocol = NULL;
+
+	protocol = g_object_new(test_purple_protocol_conversation_empty_get_type(),
+	                        NULL);
+
+	g_assert_false(purple_protocol_conversation_implements_set_title(protocol));
+
+	g_assert_finalize_object(protocol);
+}
+
+static void
+test_purple_protocol_conversation_empty_set_title_cb(GObject *source,
+                                                     GAsyncResult *result,
+                                                     G_GNUC_UNUSED gpointer data)
+{
+	PurpleProtocolConversation *protocol = NULL;
+	GError *error = NULL;
+	gboolean set = FALSE;
+
+	protocol = PURPLE_PROTOCOL_CONVERSATION(source);
+	set = purple_protocol_conversation_set_title_finish(protocol, result,
+	                                                    &error);
+	g_assert_error(error, PURPLE_PROTOCOL_CONVERSATION_DOMAIN, 0);
+	g_clear_error(&error);
+	g_assert_false(set);
+}
+
+static void
+test_purple_protocol_conversation_empty_set_title_async(void) {
+	PurpleAccount *account = NULL;
+	PurpleConversation *conversation = NULL;
+	PurpleProtocolConversation *protocol = NULL;
+
+	protocol = g_object_new(test_purple_protocol_conversation_empty_get_type(),
+	                        NULL);
+	account = purple_account_new("test", "test");
+	conversation = g_object_new(
+		PURPLE_TYPE_CONVERSATION,
+		"account", account,
+		"type", PURPLE_CONVERSATION_TYPE_DM,
+		NULL);
+
+	purple_protocol_conversation_set_title_async(protocol, conversation,
+	                                             "title", NULL,
+	                                             test_purple_protocol_conversation_empty_set_title_cb,
+	                                             NULL);
+
+	g_main_context_iteration(NULL, FALSE);
+
+	g_clear_object(&account);
+	g_assert_finalize_object(conversation);
+	g_assert_finalize_object(protocol);
+}
+
+static void
+test_purple_protocol_conversation_empty_implements_set_description(void) {
+	PurpleProtocolConversation *protocol = NULL;
+
+	protocol = g_object_new(test_purple_protocol_conversation_empty_get_type(),
+	                        NULL);
+
+	g_assert_false(purple_protocol_conversation_implements_set_description(protocol));
+
+	g_assert_finalize_object(protocol);
+}
+
+static void
+test_purple_protocol_conversation_empty_set_description_cb(GObject *source,
+                                                           GAsyncResult *result,
+                                                           G_GNUC_UNUSED gpointer data)
+{
+	PurpleProtocolConversation *protocol = NULL;
+	GError *error = NULL;
+	gboolean set = FALSE;
+
+	protocol = PURPLE_PROTOCOL_CONVERSATION(source);
+	set = purple_protocol_conversation_set_description_finish(protocol, result,
+	                                                          &error);
+	g_assert_error(error, PURPLE_PROTOCOL_CONVERSATION_DOMAIN, 0);
+	g_clear_error(&error);
+	g_assert_false(set);
+}
+
+static void
+test_purple_protocol_conversation_empty_set_description_async(void) {
+	PurpleAccount *account = NULL;
+	PurpleConversation *conversation = NULL;
+	PurpleProtocolConversation *protocol = NULL;
+
+	protocol = g_object_new(test_purple_protocol_conversation_empty_get_type(),
+	                        NULL);
+	account = purple_account_new("test", "test");
+	conversation = g_object_new(
+		PURPLE_TYPE_CONVERSATION,
+		"account", account,
+		"type", PURPLE_CONVERSATION_TYPE_DM,
+		NULL);
+
+	purple_protocol_conversation_set_description_async(protocol, conversation,
+	                                                   "description", NULL,
+	                                                   test_purple_protocol_conversation_empty_set_description_cb,
+	                                                   NULL);
+
+	g_main_context_iteration(NULL, FALSE);
+
+	g_clear_object(&account);
+	g_assert_finalize_object(conversation);
+	g_assert_finalize_object(protocol);
+}
+
 /******************************************************************************
  * TestProtocolConversation Implementation
  *****************************************************************************/
@@ -531,6 +667,12 @@
 
 	guint send_typing;
 	guint refresh;
+
+	guint set_title_async;
+	guint set_title_finish;
+
+	guint set_description_async;
+	guint set_description_finish;
 };
 
 static PurpleCreateConversationDetails *
@@ -848,6 +990,88 @@
 }
 
 static void
+test_purple_protocol_conversation_set_title_async(PurpleProtocolConversation *protocol,
+                                                  PurpleConversation *conversation,
+                                                  G_GNUC_UNUSED const char *title,
+                                                  GCancellable *cancellable,
+                                                  GAsyncReadyCallback callback,
+                                                  gpointer data)
+{
+	TestPurpleProtocolConversation *test_protocol = NULL;
+	GTask *task = NULL;
+
+	g_assert_true(PURPLE_IS_CONVERSATION(conversation));
+
+	test_protocol = TEST_PURPLE_PROTOCOL_CONVERSATION(protocol);
+	test_protocol->set_title_async += 1;
+
+	task = g_task_new(protocol, cancellable, callback, data);
+	if(test_protocol->should_error) {
+		GError *error = g_error_new_literal(TEST_PURPLE_PROTOCOL_CONVERSATION_DOMAIN,
+		                                    0, "error");
+		g_task_return_error(task, error);
+	} else {
+		g_task_return_boolean(task, TRUE);
+	}
+
+	g_clear_object(&task);
+}
+
+static gboolean
+test_purple_protocol_conversation_set_title_finish(PurpleProtocolConversation *protocol,
+                                                   GAsyncResult *result,
+                                                   GError **error)
+{
+	TestPurpleProtocolConversation *test_protocol = NULL;
+
+	test_protocol = TEST_PURPLE_PROTOCOL_CONVERSATION(protocol);
+	test_protocol->set_title_finish += 1;
+
+	return g_task_propagate_boolean(G_TASK(result), error);
+}
+
+static void
+test_purple_protocol_conversation_set_description_async(PurpleProtocolConversation *protocol,
+                                                        PurpleConversation *conversation,
+                                                        G_GNUC_UNUSED const char *description,
+                                                        GCancellable *cancellable,
+                                                        GAsyncReadyCallback callback,
+                                                        gpointer data)
+{
+	TestPurpleProtocolConversation *test_protocol = NULL;
+	GTask *task = NULL;
+
+	g_assert_true(PURPLE_IS_CONVERSATION(conversation));
+
+	test_protocol = TEST_PURPLE_PROTOCOL_CONVERSATION(protocol);
+	test_protocol->set_description_async += 1;
+
+	task = g_task_new(protocol, cancellable, callback, data);
+	if(test_protocol->should_error) {
+		GError *error = g_error_new_literal(TEST_PURPLE_PROTOCOL_CONVERSATION_DOMAIN,
+		                                    0, "error");
+		g_task_return_error(task, error);
+	} else {
+		g_task_return_boolean(task, TRUE);
+	}
+
+	g_clear_object(&task);
+}
+
+static gboolean
+test_purple_protocol_conversation_set_description_finish(PurpleProtocolConversation *protocol,
+                                                         GAsyncResult *result,
+                                                         GError **error)
+{
+	TestPurpleProtocolConversation *test_protocol = NULL;
+
+	test_protocol = TEST_PURPLE_PROTOCOL_CONVERSATION(protocol);
+	test_protocol->set_description_finish += 1;
+
+	return g_task_propagate_boolean(G_TASK(result), error);
+}
+
+static void
 test_purple_protocol_conversation_iface_init(PurpleProtocolConversationInterface *iface) {
 	iface->get_create_conversation_details = test_purple_protocol_conversation_get_create_conversation_details;
 	iface->create_conversation_async = test_purple_protocol_conversation_create_conversation_async;
@@ -871,6 +1095,12 @@
 
 	iface->send_typing = test_purple_protocol_conversation_send_typing;
 	iface->refresh = test_purple_protocol_conversation_refresh;
+
+	iface->set_title_async = test_purple_protocol_conversation_set_title_async;
+	iface->set_title_finish = test_purple_protocol_conversation_set_title_finish;
+
+	iface->set_description_async = test_purple_protocol_conversation_set_description_async;
+	iface->set_description_finish = test_purple_protocol_conversation_set_description_finish;
 }
 
 G_DEFINE_FINAL_TYPE_WITH_CODE(TestPurpleProtocolConversation,
@@ -904,6 +1134,12 @@
 
 	protocol->send_typing = 0;
 	protocol->refresh = 0;
+
+	protocol->set_title_async = 0;
+	protocol->set_title_finish = 0;
+
+	protocol->set_description_async = 0;
+	protocol->set_description_finish = 0;
 }
 
 static void
@@ -1460,6 +1696,129 @@
 }
 
 /******************************************************************************
+ * TestProtocolConversation set_title Tests
+ *****************************************************************************/
+static void
+test_purple_protocol_conversation_set_title_cb(GObject *obj,
+                                               GAsyncResult *res,
+                                               G_GNUC_UNUSED gpointer data)
+{
+	TestPurpleProtocolConversation *test_protocol = NULL;
+	PurpleProtocolConversation *protocol = NULL;
+	GError *error = NULL;
+	gboolean result = FALSE;
+
+	protocol = PURPLE_PROTOCOL_CONVERSATION(obj);
+	test_protocol = TEST_PURPLE_PROTOCOL_CONVERSATION(obj);
+
+	result = purple_protocol_conversation_set_title_finish(protocol, res,
+	                                                       &error);
+
+	if(test_protocol->should_error) {
+		g_assert_error(error, TEST_PURPLE_PROTOCOL_CONVERSATION_DOMAIN, 0);
+		g_clear_error(&error);
+		g_assert_false(result);
+	} else {
+		g_assert_no_error(error);
+		g_assert_true(result);
+	}
+}
+
+static void
+test_purple_protocol_conversation_set_title_normal(gconstpointer data) {
+	TestPurpleProtocolConversation *protocol = NULL;
+	PurpleAccount *account = NULL;
+	PurpleConversation *conversation = NULL;
+
+	protocol = g_object_new(test_purple_protocol_conversation_get_type(),
+	                        NULL);
+	protocol->should_error = GPOINTER_TO_INT(data);
+
+	account = purple_account_new("test", "test");
+	conversation = g_object_new(
+		PURPLE_TYPE_CONVERSATION,
+		"account", account,
+		"type", PURPLE_CONVERSATION_TYPE_DM,
+		NULL);
+
+	purple_protocol_conversation_set_title_async(PURPLE_PROTOCOL_CONVERSATION(protocol),
+	                                             conversation, "title", NULL,
+	                                             test_purple_protocol_conversation_set_title_cb,
+	                                             NULL);
+
+	g_main_context_iteration(NULL, FALSE);
+
+	g_assert_cmpuint(protocol->set_title_async, ==, 1);
+	g_assert_cmpuint(protocol->set_title_finish, ==, 1);
+
+	g_assert_finalize_object(conversation);
+	g_assert_finalize_object(protocol);
+	g_clear_object(&account);
+}
+
+/******************************************************************************
+ * TestProtocolConversation set_description Tests
+ *****************************************************************************/
+static void
+test_purple_protocol_conversation_set_description_cb(GObject *obj,
+                                                     GAsyncResult *res,
+                                                     G_GNUC_UNUSED gpointer data)
+{
+	TestPurpleProtocolConversation *test_protocol = NULL;
+	PurpleProtocolConversation *protocol = NULL;
+	GError *error = NULL;
+	gboolean result = FALSE;
+
+	protocol = PURPLE_PROTOCOL_CONVERSATION(obj);
+	test_protocol = TEST_PURPLE_PROTOCOL_CONVERSATION(obj);
+
+	result = purple_protocol_conversation_set_description_finish(protocol, res,
+	                                                             &error);
+
+	if(test_protocol->should_error) {
+		g_assert_error(error, TEST_PURPLE_PROTOCOL_CONVERSATION_DOMAIN, 0);
+		g_clear_error(&error);
+		g_assert_false(result);
+	} else {
+		g_assert_no_error(error);
+		g_assert_true(result);
+	}
+}
+
+static void
+test_purple_protocol_conversation_set_description_normal(gconstpointer data) {
+	TestPurpleProtocolConversation *protocol = NULL;
+	PurpleAccount *account = NULL;
+	PurpleConversation *conversation = NULL;
+
+	protocol = g_object_new(test_purple_protocol_conversation_get_type(),
+	                        NULL);
+	protocol->should_error = GPOINTER_TO_INT(data);
+
+	account = purple_account_new("test", "test");
+	conversation = g_object_new(
+		PURPLE_TYPE_CONVERSATION,
+		"account", account,
+		"type", PURPLE_CONVERSATION_TYPE_DM,
+		NULL);
+
+	purple_protocol_conversation_set_description_async(PURPLE_PROTOCOL_CONVERSATION(protocol),
+	                                                   conversation,
+	                                                   "description", NULL,
+	                                                   test_purple_protocol_conversation_set_description_cb,
+	                                                   NULL);
+
+	g_main_context_iteration(NULL, FALSE);
+
+	g_assert_cmpuint(protocol->set_description_async, ==, 1);
+	g_assert_cmpuint(protocol->set_description_finish, ==, 1);
+
+	g_assert_finalize_object(conversation);
+	g_assert_finalize_object(protocol);
+	g_clear_object(&account);
+}
+
+/******************************************************************************
  * Main
  *****************************************************************************/
 gint
@@ -1488,8 +1847,13 @@
 	g_test_add_func("/protocol-conversation/empty/send-message-async",
 	                test_purple_protocol_conversation_empty_send_message_async);
 
+	g_test_add_func("/protocol-conversation/empty/implements-set-topic",
+	                test_purple_protocol_conversation_empty_implements_set_topic);
 	g_test_add_func("/protocol-conversation/empty/set-topic-async",
 	                test_purple_protocol_conversation_empty_set_topic_async);
+
+	g_test_add_func("/protocol-conversation/empty/implements-set-avatar",
+	                test_purple_protocol_conversation_empty_implements_set_avatar);
 	g_test_add_func("/protocol-conversation/empty/set-avatar-async",
 	                test_purple_protocol_conversation_empty_set_avatar_async);
 
@@ -1499,6 +1863,16 @@
 	g_test_add_func("/protocol-conversation/empty/join_channel_async",
 	                test_purple_protocol_conversation_empty_join_channel_async);
 
+	g_test_add_func("/protocol-conversation/empty/implements-set-title",
+	                test_purple_protocol_conversation_empty_implements_set_title);
+	g_test_add_func("/protocol-conversation/empty/set-title-async",
+	                test_purple_protocol_conversation_empty_set_title_async);
+
+	g_test_add_func("/protocol-conversation/empty/implements-set-description",
+	                test_purple_protocol_conversation_empty_implements_set_description);
+	g_test_add_func("/protocol-conversation/empty/set-description-async",
+	                test_purple_protocol_conversation_empty_set_description_async);
+
 	/* Empty send typing tests. */
 	g_test_add_func("/protocol-conversation/empty/implements-send-typing",
 	                test_purple_protocol_conversation_empty_implements_send_typing);
@@ -1581,5 +1955,21 @@
 	g_test_add_func("/protocol-conversation/normal/refresh",
 	                test_purple_protocol_conversation_refresh_normal);
 
+	/* Normal set title tests. */
+	g_test_add_data_func("/protocol-contacts/normal/set-title-normal",
+	                     GINT_TO_POINTER(FALSE),
+	                     test_purple_protocol_conversation_set_title_normal);
+	g_test_add_data_func("/protocol-contacts/normal/set-title-error",
+	                     GINT_TO_POINTER(TRUE),
+	                     test_purple_protocol_conversation_set_title_normal);
+
+	/* Normal set description tests. */
+	g_test_add_data_func("/protocol-contacts/normal/set-description-normal",
+	                     GINT_TO_POINTER(FALSE),
+	                     test_purple_protocol_conversation_set_description_normal);
+	g_test_add_data_func("/protocol-contacts/normal/set-description-error",
+	                     GINT_TO_POINTER(TRUE),
+	                     test_purple_protocol_conversation_set_description_normal);
+
 	return g_test_run();
 }
