libpurple/tests/test_scheduled_task.c

changeset 43292
03fe500d5aa5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/test_scheduled_task.c	Thu Jul 24 23:33:18 2025 -0500
@@ -0,0 +1,443 @@
+/*
+ * 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 <birb.h>
+
+#include <purple.h>
+
+/******************************************************************************
+ * Tests
+ *****************************************************************************/
+static void
+test_purple_scheduled_task_new(void) {
+	PurpleScheduledTask *task = NULL;
+
+	task = purple_scheduled_task_new("test-task", "A task for testing", TRUE);
+
+	birb_assert_type(task, PURPLE_TYPE_SCHEDULED_TASK);
+
+	g_assert_finalize_object(task);
+}
+
+static void
+test_purple_scheduled_task_properties(void) {
+	PurpleScheduledTask *task = NULL;
+	PurpleScheduledTaskState state = PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED;
+	PurpleTags *tags = NULL;
+	GDateTime *execute_at = NULL;
+	gboolean cancellable = TRUE;
+	gboolean persistent = FALSE;
+	char *id = NULL;
+	char *subtitle = NULL;
+	char *task_type = NULL;
+	char *title = NULL;
+
+	task = g_object_new(
+		PURPLE_TYPE_SCHEDULED_TASK,
+		"cancellable", FALSE,
+		"id", "0xabc123",
+		"persistent", TRUE,
+		"subtitle", "a task for testing",
+		"task-type", "test",
+		"title", "Test",
+		NULL);
+
+	g_assert_true(PURPLE_IS_SCHEDULED_TASK(task));
+
+	g_object_get(
+		G_OBJECT(task),
+		"cancellable", &cancellable,
+		"execute-at", &execute_at,
+		"id", &id,
+		"persistent", &persistent,
+		"state", &state,
+		"subtitle", &subtitle,
+		"tags", &tags,
+		"task-type", &task_type,
+		"title", &title,
+		NULL);
+
+	g_assert_false(cancellable);
+
+	/* The task hasn't been scheduled so it doesn't have an execution time. */
+	g_assert_null(execute_at);
+
+	g_assert_cmpstr(id, ==, "0xabc123");
+	g_clear_pointer(&id, g_free);
+
+	g_assert_true(persistent);
+
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED);
+
+	g_assert_cmpstr(subtitle, ==, "a task for testing");
+	g_clear_pointer(&subtitle, g_free);
+
+	birb_assert_type(tags, PURPLE_TYPE_TAGS);
+	g_clear_object(&tags);
+
+	g_assert_cmpstr(task_type, ==, "test");
+	g_clear_pointer(&task_type, g_free);
+
+	g_assert_cmpstr(title, ==, "Test");
+	g_clear_pointer(&title, g_free);
+
+	g_assert_finalize_object(task);
+}
+
+/******************************************************************************
+ * Schedule Tests
+ *****************************************************************************/
+static void
+test_purple_scheduled_task_executed_counter_cb(PurpleScheduledTask *task,
+                                               gpointer data)
+{
+	PurpleScheduledTaskState state = PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED;
+	guint *counter = data;
+
+	birb_assert_type(task, PURPLE_TYPE_SCHEDULED_TASK);
+
+	state = purple_scheduled_task_get_state(task);
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_EXECUTING);
+
+	*counter = *counter + 1;
+}
+
+static void
+test_purple_scheduled_task_executed_cancel_cb(PurpleScheduledTask *task,
+                                              G_GNUC_UNUSED gpointer data)
+{
+	GError *error = NULL;
+	gboolean result = FALSE;
+
+	result = purple_scheduled_task_schedule_relative(task,
+	                                                 10 * G_TIME_SPAN_MINUTE,
+	                                                 &error);
+	g_assert_error(error,
+	               PURPLE_SCHEDULED_TASK_ERROR,
+	               PURPLE_SCHEDULED_TASK_ERROR_RESCHEDULE_EXECUTING_TASK);
+	g_clear_error(&error);
+	g_assert_false(result);
+}
+
+static void
+test_purple_scheduled_task_executed_quit_cb(PurpleScheduledTask *task,
+                                            gpointer data)
+{
+	birb_assert_type(task, PURPLE_TYPE_SCHEDULED_TASK);
+
+	g_main_loop_quit(data);
+}
+
+static void
+test_purple_scheduled_task_main_loop_timeout_cb(gpointer data) {
+	g_main_loop_quit(data);
+
+	g_assert_not_reached();
+}
+
+static void
+test_purple_scheduled_task_schedule_normal(void) {
+	PurpleScheduledTask *task = NULL;
+	PurpleScheduledTaskState state = PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED;
+	GDateTime *execute_at = NULL;
+	GError *error = NULL;
+	GMainLoop *loop = NULL;
+	guint counter = 0;
+	gboolean result = FALSE;
+
+	task = purple_scheduled_task_new("test", "Test task", TRUE);
+	g_signal_connect(task, "execute",
+	                 G_CALLBACK(test_purple_scheduled_task_executed_counter_cb),
+	                 &counter);
+
+	/* Add another signal handler to verify that you can't reschedule an
+	 * executing task.
+	 */
+	g_signal_connect(task, "execute",
+	                 G_CALLBACK(test_purple_scheduled_task_executed_cancel_cb),
+	                 NULL);
+
+	/* Verify the default state is unscheduled. */
+	state = purple_scheduled_task_get_state(task);
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED);
+
+	/* Make sure that we don't currently have an execute-at. */
+	execute_at = purple_scheduled_task_get_execute_at(task);
+	g_assert_null(execute_at);
+
+	/* Now schedule the task to execute 10 milliseconds from now. */
+	result = purple_scheduled_task_schedule_relative(task,
+	                                                 10 * G_TIME_SPAN_MILLISECOND,
+	                                                 &error);
+	g_assert_no_error(error);
+	g_assert_true(result);
+
+	/* Make sure the execute-at property got set. */
+	execute_at = purple_scheduled_task_get_execute_at(task);
+	g_assert_nonnull(execute_at);
+
+	/* Make sure the state was set to scheduled. */
+	state = purple_scheduled_task_get_state(task);
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_SCHEDULED);
+
+	/* Create our main loop and add a timeout to avoid runaways. */
+	loop = g_main_loop_new(NULL, FALSE);
+	g_signal_connect(task, "execute",
+	                 G_CALLBACK(test_purple_scheduled_task_executed_quit_cb),
+	                 loop);
+
+	g_timeout_add_seconds_once(10,
+	                           test_purple_scheduled_task_main_loop_timeout_cb,
+	                           loop);
+	g_main_loop_run(loop);
+	g_main_loop_unref(loop);
+
+	/* Make sure that the execute signal was emitted. */
+	g_assert_cmpuint(counter, ==, 1);
+
+	/* Make sure the state was updated to executed. */
+	state = purple_scheduled_task_get_state(task);
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_EXECUTED);
+
+	g_assert_finalize_object(task);
+}
+
+static void
+test_purple_scheduled_task_schedule_cancelled(void) {
+	PurpleScheduledTask *task = NULL;
+	PurpleScheduledTaskState state = PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED;
+	GError *error = NULL;
+	gboolean result = FALSE;
+
+	task = purple_scheduled_task_new("test", "Test task", TRUE);
+
+	state = purple_scheduled_task_get_state(task);
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED);
+
+	/* Cancelling a task that is not currently scheduled should do nothing. */
+	purple_scheduled_task_cancel(task);
+
+	state = purple_scheduled_task_get_state(task);
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED);
+
+	/* Schedule the task. */
+	result = purple_scheduled_task_schedule_relative(task,
+	                                                 10 * G_TIME_SPAN_MINUTE,
+	                                                 &error);
+	g_assert_no_error(error);
+	g_assert_true(result);
+
+	state = purple_scheduled_task_get_state(task);
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_SCHEDULED);
+
+	/* Cancel the task. */
+	purple_scheduled_task_cancel(task);
+
+	state = purple_scheduled_task_get_state(task);
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_CANCELLED);
+
+	g_assert_finalize_object(task);
+}
+
+static void
+test_purple_scheduled_task_schedule_reschedule(void) {
+	PurpleScheduledTask *task = NULL;
+	PurpleScheduledTaskState state = PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED;
+	GDateTime *now = NULL;
+	GDateTime *later = NULL;
+	GDateTime *execute_at = NULL;
+	GError *error = NULL;
+	gboolean result = FALSE;
+
+	task = purple_scheduled_task_new("test", "Test task", TRUE);
+
+	state = purple_scheduled_task_get_state(task);
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED);
+
+	/* Schedule the task. */
+	result = purple_scheduled_task_schedule_relative(task,
+	                                                 10 * G_TIME_SPAN_MINUTE,
+	                                                 &error);
+	g_assert_no_error(error);
+	g_assert_true(result);
+
+	state = purple_scheduled_task_get_state(task);
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_SCHEDULED);
+
+	/* Reschedule the task for an hour. */
+	now = g_date_time_new_now_local();
+	later = g_date_time_add(now, 1 * G_TIME_SPAN_HOUR);
+	g_clear_pointer(&now, g_date_time_unref);
+
+	result = purple_scheduled_task_schedule(task, later, &error);
+	g_assert_no_error(error);
+	g_assert_true(result);
+
+	/* Verify that the execute-at property is correct. */
+	execute_at = purple_scheduled_task_get_execute_at(task);
+	g_assert_true(birb_date_time_equal(execute_at, later));
+	g_clear_pointer(&later, g_date_time_unref);
+
+	state = purple_scheduled_task_get_state(task);
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_SCHEDULED);
+
+	/* Cancel the task to clear the timeout and the reference it holds. */
+	purple_scheduled_task_cancel(task);
+
+	g_assert_finalize_object(task);
+}
+
+static void
+test_purple_scheduled_task_schedule_past(void) {
+	PurpleScheduledTask *task = NULL;
+	PurpleScheduledTaskState state = PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED;
+	GError *error = NULL;
+	gboolean result = FALSE;
+
+	task = purple_scheduled_task_new("test", "Test task", TRUE);
+
+	state = purple_scheduled_task_get_state(task);
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED);
+
+	/* Schedule the task for 10 minutes ago. */
+	result = purple_scheduled_task_schedule_relative(task,
+	                                                 -10 * G_TIME_SPAN_MINUTE,
+	                                                 &error);
+	g_assert_error(error,
+	               PURPLE_SCHEDULED_TASK_ERROR,
+	               PURPLE_SCHEDULED_TASK_ERROR_EXECUTE_AT_IN_PAST);
+	g_clear_error(&error);
+	g_assert_false(result);
+
+	g_assert_finalize_object(task);
+}
+
+static void
+test_purple_scheduled_task_schedule_reuse(void) {
+	PurpleScheduledTask *task = NULL;
+	PurpleScheduledTaskState state = PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED;
+	GDateTime *execute_at = NULL;
+	GError *error = NULL;
+	GMainLoop *loop = NULL;
+	guint counter = 0;
+	gboolean result = FALSE;
+
+	task = purple_scheduled_task_new("test", "Test task", TRUE);
+	g_signal_connect(task, "execute",
+	                 G_CALLBACK(test_purple_scheduled_task_executed_counter_cb),
+	                 &counter);
+
+	/* Verify the default state is unscheduled. */
+	state = purple_scheduled_task_get_state(task);
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED);
+
+	/* Make sure that we don't currently have an execute-at. */
+	execute_at = purple_scheduled_task_get_execute_at(task);
+	g_assert_null(execute_at);
+
+	/* Now schedule the task to execute 10 milliseconds from now. */
+	result = purple_scheduled_task_schedule_relative(task,
+	                                                 10 * G_TIME_SPAN_MILLISECOND,
+	                                                 &error);
+	g_assert_no_error(error);
+	g_assert_true(result);
+
+	/* Make sure the execute-at property got set. */
+	execute_at = purple_scheduled_task_get_execute_at(task);
+	g_assert_nonnull(execute_at);
+
+	/* Make sure the state was set to scheduled. */
+	state = purple_scheduled_task_get_state(task);
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_SCHEDULED);
+
+	/* Create our main loop and add a timeout to avoid runaways. */
+	loop = g_main_loop_new(NULL, FALSE);
+	g_signal_connect(task, "execute",
+	                 G_CALLBACK(test_purple_scheduled_task_executed_quit_cb),
+	                 loop);
+
+	g_timeout_add_seconds_once(10,
+	                           test_purple_scheduled_task_main_loop_timeout_cb,
+	                           loop);
+	g_main_loop_run(loop);
+
+	/* Make sure that the execute signal was emitted. */
+	g_assert_cmpuint(counter, ==, 1);
+
+	/* Make sure the state was updated to executed. */
+	state = purple_scheduled_task_get_state(task);
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_EXECUTED);
+
+	/* Now reset everything. */
+	counter = 0;
+	g_signal_connect(task, "execute",
+	                 G_CALLBACK(test_purple_scheduled_task_executed_quit_cb),
+	                 loop);
+	g_timeout_add_seconds_once(10,
+	                           test_purple_scheduled_task_main_loop_timeout_cb,
+	                           loop);
+
+	/* Now schedule the task to execute 10 milliseconds from now. */
+	result = purple_scheduled_task_schedule_relative(task,
+	                                                 10 * G_TIME_SPAN_MILLISECOND,
+	                                                 &error);
+	g_assert_no_error(error);
+	g_assert_true(result);
+
+	/* Run the loop. */
+	g_main_loop_run(loop);
+
+	/* Make sure that the execute signal was emitted. */
+	g_assert_cmpuint(counter, ==, 1);
+
+	/* Make sure the state was updated to executed. */
+	state = purple_scheduled_task_get_state(task);
+	g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_EXECUTED);
+
+	g_main_loop_unref(loop);
+
+	g_assert_finalize_object(task);
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+int
+main(int argc, char *argv[]) {
+	g_test_init(&argc, &argv, NULL);
+	g_test_set_nonfatal_assertions();
+
+	g_test_add_func("/scheduled-task/new", test_purple_scheduled_task_new);
+	g_test_add_func("/scheduled-task/properties",
+	                test_purple_scheduled_task_properties);
+
+	g_test_add_func("/scheduled-task/schedule/normal",
+	                test_purple_scheduled_task_schedule_normal);
+	g_test_add_func("/scheduled-task/schedule/cancelled",
+	                test_purple_scheduled_task_schedule_cancelled);
+	g_test_add_func("/scheduled-task/schedule/reschedule",
+	                test_purple_scheduled_task_schedule_reschedule);
+	g_test_add_func("/scheduled-task/schedule/past",
+	                test_purple_scheduled_task_schedule_past);
+	g_test_add_func("/scheduled-task/schedule/reuse",
+	                test_purple_scheduled_task_schedule_reuse);
+
+	return g_test_run();
+}

mercurial