gtk2/gtk/tests/recentmanager.c
Emmanuele Bassi ce5a29bc38 recent-manager: Coalesce multiple changes
Since the ::changed implementation of GtkRecentManager implies a
synchronous write operation, when we receive multiple requests to emit a
::changed signal we might end up blocking.

This change coalesces multiple ::changed emission requests using the
following sequence:

  • the first request will install a timeout in 250 ms, which will
    emit the ::changed signal

  • each further request while the timeout has not been emitted
    will increase a counter

      ‣ if the counter reaches 250 before the timeout has been
        emitted, then the RecentManager will remove the timeout
        source and force a signal emission and reset the counter

This sequence should guarantee that frequent ::changed emission requests
are coalesced, and also guarantee that we don't let them dangle for too
long.

https://bugzilla.gnome.org/show_bug.cgi?id=616997
2010-10-22 18:08:48 +01:00

312 lines
8.8 KiB
C

/* GTK - The GIMP Toolkit
* gtkrecentmanager.c: a manager for the recently used resources
*
* Copyright (C) 2006 Emmanuele Bassi
*
* 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, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <glib/gstdio.h>
#include <gtk/gtk.h>
const gchar *uri = "file:///tmp/testrecentchooser.txt";
const gchar *uri2 = "file:///tmp/testrecentchooser2.txt";
static void
recent_manager_get_default (void)
{
GtkRecentManager *manager;
GtkRecentManager *manager2;
manager = gtk_recent_manager_get_default ();
g_assert (manager != NULL);
manager2 = gtk_recent_manager_get_default ();
g_assert (manager == manager2);
}
static void
recent_manager_add (void)
{
GtkRecentManager *manager;
GtkRecentData *recent_data;
gboolean res;
manager = gtk_recent_manager_get_default ();
recent_data = g_slice_new0 (GtkRecentData);
/* mime type is mandatory */
recent_data->mime_type = NULL;
recent_data->app_name = "testrecentchooser";
recent_data->app_exec = "testrecentchooser %u";
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
{
res = gtk_recent_manager_add_full (manager,
uri,
recent_data);
}
g_test_trap_assert_failed ();
/* app name is mandatory */
recent_data->mime_type = "text/plain";
recent_data->app_name = NULL;
recent_data->app_exec = "testrecentchooser %u";
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
{
res = gtk_recent_manager_add_full (manager,
uri,
recent_data);
}
g_test_trap_assert_failed ();
/* app exec is mandatory */
recent_data->mime_type = "text/plain";
recent_data->app_name = "testrecentchooser";
recent_data->app_exec = NULL;
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
{
res = gtk_recent_manager_add_full (manager,
uri,
recent_data);
}
g_test_trap_assert_failed ();
recent_data->mime_type = "text/plain";
recent_data->app_name = "testrecentchooser";
recent_data->app_exec = "testrecentchooser %u";
res = gtk_recent_manager_add_full (manager,
uri,
recent_data);
g_assert (res == TRUE);
g_slice_free (GtkRecentData, recent_data);
}
typedef struct {
GMainLoop *main_loop;
gint counter;
} AddManyClosure;
static void
check_bulk (GtkRecentManager *manager,
gpointer data)
{
AddManyClosure *closure = data;
if (g_test_verbose ())
g_print (G_STRLOC ": counter = %d\n", closure->counter);
g_assert_cmpint (closure->counter, ==, 100);
if (g_main_loop_is_running (closure->main_loop))
g_main_loop_quit (closure->main_loop);
}
static void
recent_manager_add_many (void)
{
GtkRecentManager *manager = g_object_new (GTK_TYPE_RECENT_MANAGER,
"filename", "recently-used.xbel",
NULL);
AddManyClosure *closure = g_new (AddManyClosure, 1);
GtkRecentData *data = g_slice_new0 (GtkRecentData);
gint i;
closure->main_loop = g_main_loop_new (NULL, FALSE);
closure->counter = 0;
g_signal_connect (manager, "changed", G_CALLBACK (check_bulk), closure);
for (i = 0; i < 100; i++)
{
gchar *new_uri;
data->mime_type = "text/plain";
data->app_name = "testrecentchooser";
data->app_exec = "testrecentchooser %u";
if (g_test_verbose ())
g_print (G_STRLOC ": adding item %d\n", i);
new_uri = g_strdup_printf ("file:///doesnotexist-%d.txt", i);
gtk_recent_manager_add_full (manager, new_uri, data);
g_free (new_uri);
closure->counter += 1;
}
g_main_loop_run (closure->main_loop);
g_main_loop_unref (closure->main_loop);
g_slice_free (GtkRecentData, data);
g_free (closure);
g_object_unref (manager);
g_assert_cmpint (g_unlink ("recently-used.xbel"), ==, 0);
}
static void
recent_manager_has_item (void)
{
GtkRecentManager *manager;
gboolean res;
manager = gtk_recent_manager_get_default ();
res = gtk_recent_manager_has_item (manager, "file:///tmp/testrecentdoesnotexist.txt");
g_assert (res == FALSE);
res = gtk_recent_manager_has_item (manager, uri);
g_assert (res == TRUE);
}
static void
recent_manager_move_item (void)
{
GtkRecentManager *manager;
gboolean res;
GError *error;
manager = gtk_recent_manager_get_default ();
error = NULL;
res = gtk_recent_manager_move_item (manager,
"file:///tmp/testrecentdoesnotexist.txt",
uri2,
&error);
g_assert (res == FALSE);
g_assert (error != NULL);
g_assert (error->domain == GTK_RECENT_MANAGER_ERROR);
g_assert (error->code == GTK_RECENT_MANAGER_ERROR_NOT_FOUND);
g_error_free (error);
error = NULL;
res = gtk_recent_manager_move_item (manager, uri, uri2, &error);
g_assert (res == TRUE);
g_assert (error == NULL);
res = gtk_recent_manager_has_item (manager, uri);
g_assert (res == FALSE);
res = gtk_recent_manager_has_item (manager, uri2);
g_assert (res == TRUE);
}
static void
recent_manager_lookup_item (void)
{
GtkRecentManager *manager;
GtkRecentInfo *info;
GError *error;
manager = gtk_recent_manager_get_default ();
error = NULL;
info = gtk_recent_manager_lookup_item (manager,
"file:///tmp/testrecentdoesnotexist.txt",
&error);
g_assert (info == NULL);
g_assert (error != NULL);
g_assert (error->domain == GTK_RECENT_MANAGER_ERROR);
g_assert (error->code == GTK_RECENT_MANAGER_ERROR_NOT_FOUND);
g_error_free (error);
error = NULL;
info = gtk_recent_manager_lookup_item (manager, uri2, &error);
g_assert (info != NULL);
g_assert (error == NULL);
g_assert (gtk_recent_info_has_application (info, "testrecentchooser"));
gtk_recent_info_unref (info);
}
static void
recent_manager_remove_item (void)
{
GtkRecentManager *manager;
gboolean res;
GError *error;
manager = gtk_recent_manager_get_default ();
error = NULL;
res = gtk_recent_manager_remove_item (manager,
"file:///tmp/testrecentdoesnotexist.txt",
&error);
g_assert (res == FALSE);
g_assert (error != NULL);
g_assert (error->domain == GTK_RECENT_MANAGER_ERROR);
g_assert (error->code == GTK_RECENT_MANAGER_ERROR_NOT_FOUND);
g_error_free (error);
/* remove an item that's actually there */
error = NULL;
res = gtk_recent_manager_remove_item (manager, uri2, &error);
g_assert (res == TRUE);
g_assert (error == NULL);
res = gtk_recent_manager_has_item (manager, uri2);
g_assert (res == FALSE);
}
static void
recent_manager_purge (void)
{
GtkRecentManager *manager;
GtkRecentData *recent_data;
gint n;
GError *error;
manager = gtk_recent_manager_get_default ();
/* purge, add 1, purge again and check that 1 item has been purged */
error = NULL;
n = gtk_recent_manager_purge_items (manager, &error);
g_assert (error == NULL);
recent_data = g_slice_new0 (GtkRecentData);
recent_data->mime_type = "text/plain";
recent_data->app_name = "testrecentchooser";
recent_data->app_exec = "testrecentchooser %u";
gtk_recent_manager_add_full (manager, uri, recent_data);
g_slice_free (GtkRecentData, recent_data);
error = NULL;
n = gtk_recent_manager_purge_items (manager, &error);
g_assert (error == NULL);
g_assert (n == 1);
}
int
main (int argc,
char **argv)
{
gtk_test_init (&argc, &argv, NULL);
g_test_add_func ("/recent-manager/get-default", recent_manager_get_default);
g_test_add_func ("/recent-manager/add", recent_manager_add);
g_test_add_func ("/recent-manager/add-many", recent_manager_add_many);
g_test_add_func ("/recent-manager/has-item", recent_manager_has_item);
g_test_add_func ("/recent-manager/move-item", recent_manager_move_item);
g_test_add_func ("/recent-manager/lookup-item", recent_manager_lookup_item);
g_test_add_func ("/recent-manager/remove-item", recent_manager_remove_item);
g_test_add_func ("/recent-manager/purge", recent_manager_purge);
return g_test_run ();
}