Sun, 10 Aug 2025 23:44:08 +0800
Add Purple.Conversation.find_message_by_id
The method was added so that a protocol or plugin could easily lookup
for the reference for a message. This will be especially useful when a
protocol received a quoted message but only with an id.
/* * 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_scheduler_new(void) { PurpleScheduler *scheduler = NULL; scheduler = purple_scheduler_new(); birb_assert_type(scheduler, PURPLE_TYPE_SCHEDULER); g_assert_true(G_IS_LIST_MODEL(scheduler)); g_assert_finalize_object(scheduler); } static void test_purple_scheduler_properties(void) { PurpleScheduler *scheduler = NULL; GType item_type = G_TYPE_INVALID; guint n_items = 0; scheduler = g_object_new( PURPLE_TYPE_SCHEDULER, NULL); g_object_get( G_OBJECT(scheduler), "item-type", &item_type, "n-items", &n_items, NULL); g_assert_cmpuint(item_type, ==, PURPLE_TYPE_SCHEDULED_TASK); g_assert_cmpuint(n_items, ==, 0); g_assert_finalize_object(scheduler); } static void test_purple_scheduler_add_remove(void) { PurpleScheduledTask *task = NULL; PurpleScheduledTaskState state = PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED; PurpleScheduler *scheduler = NULL; GDateTime *execute_at = NULL; GDateTime *now = NULL; GError *error = NULL; guint counter = 0; gboolean result = FALSE; scheduler = purple_scheduler_new(); birb_assert_type(scheduler, PURPLE_TYPE_SCHEDULER); g_assert_true(G_IS_LIST_MODEL(scheduler)); /* Create our execute_at time. */ now = g_date_time_new_now_local(); execute_at = g_date_time_add(now, 10 * G_TIME_SPAN_MINUTE); g_clear_pointer(&now, g_date_time_unref); /* Wire up our signals. */ birb_count_list_model_items_changed(G_LIST_MODEL(scheduler), &counter); /* Create the task. */ task = purple_scheduled_task_new("test-scheduler", "Scheduler Tests", TRUE); birb_assert_type(task, PURPLE_TYPE_SCHEDULED_TASK); /* Make sure the task is unscheduled. */ state = purple_scheduled_task_get_state(task); g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED); /* Add the task to the scheduler. */ counter = 0; result = purple_scheduler_add_task(scheduler, task, execute_at, &error); g_assert_no_error(error); g_assert_true(result); /* Make sure items changed was called once and that we have 1 item in the * list model. */ g_assert_cmpuint(counter, ==, 1); birb_assert_list_model_n_items(scheduler, 1); /* Make sure that the task got scheduled. */ state = purple_scheduled_task_get_state(task); g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_SCHEDULED); /* Remove the task. */ counter = 0; result = purple_scheduler_remove_task(scheduler, purple_scheduled_task_get_id(task)); g_assert_true(result); /* Make sure items changed was called once and that the model empty. */ g_assert_cmpuint(counter, ==, 1); birb_assert_list_model_n_items(scheduler, 0); /* Make sure the that the task got cancelled. */ state = purple_scheduled_task_get_state(task); g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_CANCELLED); /* After removal from the scheduler, only our reference should exist. */ g_assert_finalize_object(task); /* Clean up the scheduler. */ g_assert_finalize_object(scheduler); g_clear_pointer(&execute_at, g_date_time_unref); } static void test_purple_scheduler_add_already_scheduled(void) { PurpleScheduledTask *task = NULL; PurpleScheduler *scheduler = NULL; GDateTime *original_execute_at = NULL; GDateTime *updated_execute_at = NULL; GError *error = NULL; gboolean result = FALSE; scheduler = purple_scheduler_new(); /* Create the task. */ task = purple_scheduled_task_new("test-scheduler", "Scheduler Tests", TRUE); /* Schedule the task and store the original execute_at. */ result = purple_scheduled_task_schedule_relative(task, 10 * G_TIME_SPAN_MILLISECOND, &error); g_assert_no_error(error); g_assert_true(result); original_execute_at = purple_scheduled_task_get_execute_at(task); if(original_execute_at != NULL) { g_date_time_ref(original_execute_at); } /* Add the task to the scheduler. */ result = purple_scheduler_add_task_relative(scheduler, task, 100 * G_TIME_SPAN_MILLISECOND, &error); g_assert_no_error(error); g_assert_true(result); /* Get the execute time of the task and verify that it is not the same as * the original time. */ updated_execute_at = purple_scheduled_task_get_execute_at(task); g_assert_false(birb_date_time_equal(original_execute_at, updated_execute_at)); /* Clean up everything. */ g_assert_finalize_object(scheduler); g_assert_finalize_object(task); g_clear_pointer(&original_execute_at, g_date_time_unref); } static void test_purple_scheduler_double_add(void) { PurpleScheduledTask *task = NULL; PurpleScheduledTaskState state = PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED; PurpleScheduler *scheduler = NULL; GDateTime *execute_at = NULL; GDateTime *now = NULL; GError *error = NULL; guint counter = 0; gboolean result = FALSE; scheduler = purple_scheduler_new(); birb_assert_type(scheduler, PURPLE_TYPE_SCHEDULER); g_assert_true(G_IS_LIST_MODEL(scheduler)); /* Create our execute_at time. */ now = g_date_time_new_now_local(); execute_at = g_date_time_add(now, 10 * G_TIME_SPAN_MINUTE); g_clear_pointer(&now, g_date_time_unref); /* Wire up our signals. */ birb_count_list_model_items_changed(G_LIST_MODEL(scheduler), &counter); /* Create the task. */ task = purple_scheduled_task_new("test-scheduler", "Scheduler Tests", TRUE); birb_assert_type(task, PURPLE_TYPE_SCHEDULED_TASK); /* Make sure the task is unscheduled. */ state = purple_scheduled_task_get_state(task); g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED); /* Add the task to the scheduler. */ counter = 0; result = purple_scheduler_add_task(scheduler, task, execute_at, &error); g_assert_no_error(error); g_assert_true(result); /* Make sure items changed was called once and that we have 1 item in the * list model. */ g_assert_cmpuint(counter, ==, 1); birb_assert_list_model_n_items(scheduler, 1); /* Make sure that the task got scheduled. */ state = purple_scheduled_task_get_state(task); g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_SCHEDULED); /* Now add the task again. */ counter = 0; result = purple_scheduler_add_task(scheduler, task, execute_at, &error); g_assert_error(error, PURPLE_SCHEDULER_ERROR, PURPLE_SCHEDULER_ERROR_TASK_EXISTS); g_clear_error(&error); g_assert_false(result); /* Make sure the items-changed signal wasn't called and that we still only * have one item in the list. */ g_assert_cmpuint(counter, ==, 0); birb_assert_list_model_n_items(scheduler, 1); /* Cleanup. We don't remove the task because we want to make sure the * scheduler will cancel it when it shuts down. Also the task is last as * it's still known to the scheduler. */ g_assert_finalize_object(scheduler); state = purple_scheduled_task_get_state(task); g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_CANCELLED); g_assert_finalize_object(task); g_clear_pointer(&execute_at, g_date_time_unref); } static void test_purple_scheduler_double_remove(void) { PurpleScheduledTask *task = NULL; PurpleScheduledTaskState state = PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED; PurpleScheduler *scheduler = NULL; GDateTime *execute_at = NULL; GDateTime *now = NULL; GError *error = NULL; guint counter = 0; gboolean result = FALSE; scheduler = purple_scheduler_new(); birb_assert_type(scheduler, PURPLE_TYPE_SCHEDULER); g_assert_true(G_IS_LIST_MODEL(scheduler)); /* Create our execute_at time. */ now = g_date_time_new_now_local(); execute_at = g_date_time_add(now, 10 * G_TIME_SPAN_MINUTE); g_clear_pointer(&now, g_date_time_unref); /* Wire up our signals. */ birb_count_list_model_items_changed(G_LIST_MODEL(scheduler), &counter); /* Create the task. */ task = purple_scheduled_task_new("test-scheduler", "Scheduler Tests", TRUE); birb_assert_type(task, PURPLE_TYPE_SCHEDULED_TASK); /* Make sure the task is unscheduled. */ state = purple_scheduled_task_get_state(task); g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED); /* Add the task to the scheduler. */ counter = 0; result = purple_scheduler_add_task(scheduler, task, execute_at, &error); g_assert_no_error(error); g_assert_true(result); /* Make sure items changed was called once and that we have 1 item in the * list model. */ g_assert_cmpuint(counter, ==, 1); birb_assert_list_model_n_items(scheduler, 1); /* Make sure that the task got scheduled. */ state = purple_scheduled_task_get_state(task); g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_SCHEDULED); /* Remove the task. */ counter = 0; result = purple_scheduler_remove_task(scheduler, purple_scheduled_task_get_id(task)); g_assert_true(result); /* Make sure the items-changed signal got called once and that we no longer * have any items in the model. */ g_assert_cmpuint(counter, ==, 1); birb_assert_list_model_n_items(scheduler, 0); /* Attempt to remove the task again. */ counter = 0; result = purple_scheduler_remove_task(scheduler, purple_scheduled_task_get_id(task)); g_assert_false(result); /* Make sure the items-changed signal wasn't called and that we still don't * have any items in the model. */ g_assert_cmpuint(counter, ==, 0); birb_assert_list_model_n_items(scheduler, 0); /* Cleanup. We remove task first because the scheduler shouldn't know about * it anymore. */ g_assert_finalize_object(task); g_assert_finalize_object(scheduler); g_clear_pointer(&execute_at, g_date_time_unref); } /****************************************************************************** * Main *****************************************************************************/ static void test_purple_scheduler_execute_task_cb(PurpleScheduler *scheduler, PurpleScheduledTask *task, const char *task_type, gpointer data) { PurpleScheduledTaskState state = PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED; const char *actual_task_type = NULL; guint *counter = data; birb_assert_type(scheduler, PURPLE_TYPE_SCHEDULER); state = purple_scheduled_task_get_state(task); g_assert_cmpuint(state, ==, PURPLE_SCHEDULED_TASK_STATE_EXECUTING); actual_task_type = purple_scheduled_task_get_task_type(task); g_assert_cmpstr(actual_task_type, ==, task_type); *counter = *counter + 1; } static void test_purple_scheduler_signals_quit_cb(G_GNUC_UNUSED PurpleScheduler *scheduler, G_GNUC_UNUSED PurpleScheduledTask *task, G_GNUC_UNUSED const char *task_type, gpointer data) { g_main_loop_quit(data); } static void test_purple_scheduler_signals_timeout_cb(gpointer data) { g_main_loop_quit(data); g_assert_not_reached(); } static void test_purple_scheduler_signals(void) { PurpleScheduledTask *task1 = NULL; PurpleScheduledTask *task2 = NULL; PurpleScheduler *scheduler = NULL; GError *error = NULL; GMainLoop *loop = NULL; guint all_counter = 0; guint detailed_counter = 0; gboolean result = FALSE; /* This test creates 2 tasks, one for 10ms from now and the second for 20ms * from now. When the 20ms task is executed the main loop will be quit and * allow the rest of the test to finish. * * There is a 2 second timeout to make sure we don't hang the unit tests if * something unexpected happens. We use 2 seconds because internally the * tasks are scheduled with g_timeout_add_seconds which tries to schedule * timeouts together to avoid excessive CPU wake ups, so 2 seconds should * cover that. */ /* Create the counter and add our signal handlers with and without the * detail. */ scheduler = purple_scheduler_new(); g_signal_connect(scheduler, "execute-task", G_CALLBACK(test_purple_scheduler_execute_task_cb), &all_counter); g_signal_connect(scheduler, "execute-task::scheduler-test-2", G_CALLBACK(test_purple_scheduler_execute_task_cb), &detailed_counter); /* Add the first task. */ task1 = purple_scheduled_task_new("scheduler-test-1", "Scheduler Test 1", TRUE); result = purple_scheduler_add_task_relative(scheduler, task1, 10 * G_TIME_SPAN_MILLISECOND, &error); g_assert_no_error(error); g_assert_true(result); /* Add the second task. */ task2 = purple_scheduled_task_new("scheduler-test-2", "Scheduler Test 2", FALSE); result = purple_scheduler_add_task_relative(scheduler, task2, 20 * G_TIME_SPAN_MILLISECOND, &error); g_assert_no_error(error); g_assert_true(result); /* Create the main loop to run the tasks. */ loop = g_main_loop_new(NULL, FALSE); /* Add a handler to quit the main loop when the second task is executed. */ g_signal_connect(scheduler, "execute-task::scheduler-test-2", G_CALLBACK(test_purple_scheduler_signals_quit_cb), loop); /* Add a timeout to avoid hangs on unexpected behavior. */ g_timeout_add_seconds_once(2, test_purple_scheduler_signals_timeout_cb, loop); /* Run the main loop. */ g_main_loop_run(loop); g_clear_pointer(&loop, g_main_loop_unref); /* Make sure the counters are correct. */ g_assert_cmpuint(all_counter, ==, 2); g_assert_cmpuint(detailed_counter, ==, 1); /* Make sure the scheduler is empty. */ birb_assert_list_model_n_items(scheduler, 0); g_assert_finalize_object(scheduler); g_assert_finalize_object(task1); g_assert_finalize_object(task2); } /****************************************************************************** * Main *****************************************************************************/ int main(int argc, char *argv[]) { g_test_init(&argc, &argv, NULL); g_test_set_nonfatal_assertions(); g_test_add_func("/scheduler/new", test_purple_scheduler_new); g_test_add_func("/scheduler/properties", test_purple_scheduler_properties); g_test_add_func("/scheduler/add-remove", test_purple_scheduler_add_remove); g_test_add_func("/scheduler/add-already-scheduled", test_purple_scheduler_add_already_scheduled); g_test_add_func("/scheduler/double-add", test_purple_scheduler_double_add); g_test_add_func("/scheduler/double-remove", test_purple_scheduler_double_remove); g_test_add_func("/scheduler/signals", test_purple_scheduler_signals); return g_test_run(); }