diff --git a/data/leak-suppress.txt b/data/leak-suppress.txt index c40bc9c02107c32f803e08348b7c2631f4eb9806..ae4d1f9924777f54009352576f7651fab2e733bc 100644 --- a/data/leak-suppress.txt +++ b/data/leak-suppress.txt @@ -7,4 +7,5 @@ leak:gtk_init leak:adw_init leak:xdg_mime_init leak:gtk_at_context_create -leak:libim-ibus.so \ No newline at end of file +leak:libim-ibus.so +leak:gtk_label_set_mnemonic_widget diff --git a/src/nautilus-file.c b/src/nautilus-file.c index 962c27125426f1c797501cb5fe21f9a2d7b3ad75..e3410497dfec55f2749319b3eef5c0b092be31cf 100644 --- a/src/nautilus-file.c +++ b/src/nautilus-file.c @@ -8226,7 +8226,7 @@ static GList *ready_data_list = NULL; typedef struct { GList *file_list; - GList *remaining_files; + GHashTable *remaining_files; NautilusFileListCallback callback; gpointer callback_data; } FileListReadyData; @@ -8242,7 +8242,7 @@ file_list_ready_data_free (FileListReadyData *data) ready_data_list = g_list_delete_link (ready_data_list, l); nautilus_file_list_free (data->file_list); - g_list_free (data->remaining_files); + g_hash_table_unref (data->remaining_files); g_free (data); } } @@ -8256,10 +8256,15 @@ file_list_ready_data_new (GList *file_list, data = g_new0 (FileListReadyData, 1); data->file_list = nautilus_file_list_copy (file_list); - data->remaining_files = g_list_copy (file_list); + data->remaining_files = g_hash_table_new (NULL, NULL); data->callback = callback; data->callback_data = callback_data; + for (GList *l = file_list; l != NULL; l = l->next) + { + g_hash_table_add (data->remaining_files, l->data); + } + ready_data_list = g_list_prepend (ready_data_list, data); return data; @@ -8272,9 +8277,9 @@ file_list_file_ready_callback (NautilusFile *file, FileListReadyData *data; data = user_data; - data->remaining_files = g_list_remove (data->remaining_files, file); + g_hash_table_remove (data->remaining_files, file); - if (data->remaining_files == NULL) + if (g_hash_table_size (data->remaining_files) == 0) { if (data->callback) { @@ -8334,9 +8339,11 @@ nautilus_file_list_cancel_call_when_ready (NautilusFileListHandle *handle) l = g_list_find (ready_data_list, data); if (l != NULL) { - for (l = data->remaining_files; l != NULL; l = l->next) + g_autoptr (GPtrArray) remaining_files = g_hash_table_steal_all_keys (data->remaining_files); + + for (guint i = 0; i < remaining_files->len; i++) { - file = NAUTILUS_FILE (l->data); + file = NAUTILUS_FILE (remaining_files->pdata[i]); NAUTILUS_FILE_CLASS (G_OBJECT_GET_CLASS (file))->cancel_call_when_ready (file, file_list_file_ready_callback, data); diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c index b42530dce4161ef7750538de6da8a8f3376107e7..b661f247be61fcf400c3d75cf28979bcd6884589 100644 --- a/src/nautilus-files-view.c +++ b/src/nautilus-files-view.c @@ -1217,7 +1217,7 @@ file_and_directory_get_file (FileAndDirectory *fad) { g_return_val_if_fail (fad != NULL, NULL); - return nautilus_file_ref (fad->file); + return fad->file; } static void @@ -4210,6 +4210,16 @@ still_should_show_file (NautilusFilesView *view, view_file_still_belongs (view, fad); } +static gboolean +still_should_add_file (NautilusFilesView *view, + FileAndDirectory *fad) +{ + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (view); + + return still_should_show_file (view, fad) && + nautilus_view_model_get_item_for_file (priv->model, fad->file) == NULL; +} + static void real_end_file_changes (NautilusFilesView *view) { @@ -4330,7 +4340,7 @@ real_remove_files (NautilusFilesView *self, } } - if (items != NULL) + if (g_hash_table_size (items) > 0) { nautilus_view_model_remove_items (priv->model, items, directory); } @@ -4391,32 +4401,29 @@ process_pending_files (NautilusFilesView *view) for (GList *node = files_added; node != NULL; node = node->next) { + gboolean should_add_file; pending = node->data; - if (nautilus_file_is_gone (pending->file)) - { - if (g_getenv ("G_MESSAGES_DEBUG") == NULL) - { - g_warning ("Attempted to add a non-existent file to the view."); - } - else - { - g_autofree char *uri = nautilus_file_get_uri (pending->file); - g_warning ("Attempted to add non-existent file \"%s\" to the view.", uri); - } - continue; - } - if (!nautilus_files_view_should_show_file (view, pending->file)) + should_add_file = still_should_add_file (view, pending); + if (should_add_file) { - continue; + pending_additions = g_list_prepend (pending_additions, pending->file); } - pending_additions = g_list_prepend (pending_additions, pending->file); + /* Acknowledge the files that were pending to be revealed */ if (g_hash_table_contains (priv->pending_reveal, pending->file)) { - g_hash_table_insert (priv->pending_reveal, - pending->file, - GUINT_TO_POINTER (TRUE)); + if (should_add_file) + { + g_hash_table_insert (priv->pending_reveal, + pending->file, + GUINT_TO_POINTER (TRUE)); + } + else + { + g_hash_table_remove (priv->pending_reveal, + pending->file); + } } } pending_additions = g_list_reverse (pending_additions); @@ -4482,7 +4489,7 @@ process_pending_files (NautilusFilesView *view) files = g_list_copy_deep (files_changed, (GCopyFunc) file_and_directory_get_file, NULL); send_selection_change = _g_lists_sort_and_check_for_intersection (&files, &selection); - nautilus_file_list_free (files); + g_list_free (files); } if (send_selection_change) diff --git a/src/nautilus-properties-window.c b/src/nautilus-properties-window.c index 2067f972ddc318a2f48df45da8d58875bc2b0664..f5e9fbefd3fb62094a21e7e65b6a7bb5329e57de 100644 --- a/src/nautilus-properties-window.c +++ b/src/nautilus-properties-window.c @@ -414,9 +414,9 @@ typedef struct GtkWindow *parent_window; char *startup_id; char *pending_key; - GHashTable *pending_files; NautilusPropertiesWindowCallback callback; gpointer callback_data; + NautilusFileListHandle *handle; NautilusPropertiesWindow *window; gboolean cancelled; } StartupData; @@ -448,8 +448,8 @@ static void value_row_update (AdwActionRow *row, NautilusPropertiesWindow *self); static void properties_window_update (NautilusPropertiesWindow *self, GList *files); -static void is_directory_ready_callback (NautilusFile *file, - gpointer data); +static void is_directory_ready_callback (GList *file_list, + gpointer data); static void cancel_group_change_callback (GroupChange *change); static void cancel_owner_change_callback (OwnerChange *change); static gboolean all_can_set_permissions (GList *file_list); @@ -3643,7 +3643,6 @@ startup_data_new (GList *files, NautilusPropertiesWindow *window) { StartupData *data; - GList *l; data = g_new0 (StartupData, 1); data->files = nautilus_file_list_copy (files); @@ -3651,17 +3650,10 @@ startup_data_new (GList *files, data->parent_window = parent_window; data->startup_id = g_strdup (startup_id); data->pending_key = g_strdup (pending_key); - data->pending_files = g_hash_table_new (g_direct_hash, - g_direct_equal); data->callback = callback; data->callback_data = callback_data; data->window = window; - for (l = data->files; l != NULL; l = l->next) - { - g_hash_table_insert (data->pending_files, l->data, l->data); - } - return data; } @@ -3669,7 +3661,6 @@ static void startup_data_free (StartupData *data) { nautilus_file_list_free (data->files); - g_hash_table_destroy (data->pending_files); g_free (data->pending_key); g_free (data->startup_id); g_free (data); @@ -3783,17 +3774,6 @@ parent_widget_destroyed_callback (GtkWidget *widget, properties_window_finish ((StartupData *) callback_data); } -static void -cancel_call_when_ready_callback (gpointer key, - gpointer value, - gpointer user_data) -{ - nautilus_file_cancel_call_when_ready - (NAUTILUS_FILE (key), - is_directory_ready_callback, - user_data); -} - static void remove_pending (StartupData *startup_data, gboolean cancel_call_when_ready, @@ -3801,9 +3781,7 @@ remove_pending (StartupData *startup_data, { if (cancel_call_when_ready) { - g_hash_table_foreach (startup_data->pending_files, - cancel_call_when_ready_callback, - startup_data); + nautilus_file_list_cancel_call_when_ready (startup_data->handle); } if (cancel_timed_wait) { @@ -3834,32 +3812,22 @@ widget_on_destroy (GtkWidget *widget, } static void -is_directory_ready_callback (NautilusFile *file, - gpointer data) +is_directory_ready_callback (GList *file_list, + gpointer data) { - StartupData *startup_data; + StartupData *startup_data = data; + NautilusPropertiesWindow *new_window = create_properties_window (startup_data); - startup_data = data; + startup_data->window = new_window; - g_hash_table_remove (startup_data->pending_files, file); + remove_pending (startup_data, FALSE, TRUE); - if (g_hash_table_size (startup_data->pending_files) == 0) - { - NautilusPropertiesWindow *new_window; - - new_window = create_properties_window (startup_data); - - startup_data->window = new_window; - - remove_pending (startup_data, FALSE, TRUE); + adw_dialog_present (ADW_DIALOG (new_window), GTK_WIDGET (startup_data->parent_window)); + g_signal_connect (GTK_WIDGET (new_window), "destroy", + G_CALLBACK (widget_on_destroy), startup_data); - adw_dialog_present (ADW_DIALOG (new_window), GTK_WIDGET (startup_data->parent_window)); - g_signal_connect (GTK_WIDGET (new_window), "destroy", - G_CALLBACK (widget_on_destroy), startup_data); - - /* We wish the label to be selectable, but not selected by default. */ - gtk_label_select_region (GTK_LABEL (new_window->name_value_label), -1, -1); - } + /* We wish the label to be selectable, but not selected by default. */ + gtk_label_select_region (GTK_LABEL (new_window->name_value_label), -1, -1); } void @@ -3869,7 +3837,6 @@ nautilus_properties_window_present (GList *files, NautilusPropertiesWindowCallback callback, gpointer callback_data) { - GList *l, *next; GtkWindow *parent_window; StartupData *startup_data; g_autofree char *pending_key = NULL; @@ -3928,15 +3895,11 @@ nautilus_properties_window_present (GList *files, _("Creating Properties window."), parent_window == NULL ? NULL : GTK_WINDOW (parent_window)); - for (l = startup_data->files; l != NULL; l = next) - { - next = l->next; - nautilus_file_call_when_ready - (NAUTILUS_FILE (l->data), - NAUTILUS_FILE_ATTRIBUTE_INFO, - is_directory_ready_callback, - startup_data); - } + nautilus_file_list_call_when_ready (startup_data->files, + NAUTILUS_FILE_ATTRIBUTE_INFO, + &startup_data->handle, + is_directory_ready_callback, + startup_data); } static void diff --git a/test/automated/display/meson.build b/test/automated/display/meson.build index c0506f142234bd320e1ab235deb08faac5f08d1e..c7742d7ededb9f011ce6a92b088b3c97f28a75b2 100644 --- a/test/automated/display/meson.build +++ b/test/automated/display/meson.build @@ -1,9 +1,7 @@ -test_env += [ - 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), - 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()) -] - tests = [ + ['test-files-view', [ + 'test-files-view.c' + ]], ['test-nautilus-directory-async', [ 'test-nautilus-directory-async.c' ]], @@ -12,7 +10,7 @@ tests = [ foreach t: tests test( t[0], - executable(t[0], t[1], dependencies: libnautilus_dep), + executable(t[0], t[1], dependencies: [libnautilus_dep, libtestutils_dep]), env: [ test_env, 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), diff --git a/test/automated/display/test-files-view.c b/test/automated/display/test-files-view.c new file mode 100644 index 0000000000000000000000000000000000000000..0a429272631f9a0913b526ad25ce1eaea257ae1a --- /dev/null +++ b/test/automated/display/test-files-view.c @@ -0,0 +1,370 @@ +/* + * Copyright © 2025 Khalid Abu Shawarib + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include + +#undef G_LOG_DOMAIN +#include +#undef G_LOG_DOMAIN +#include + +#define G_LOG_DOMAIN "test-files-view" + +static gboolean +ptr_arrays_equal_unordered (GPtrArray *a, + GPtrArray *b) +{ + if (a->len != b->len) + { + return FALSE; + } + + for (guint i = 0; i < a->len; i++) + { + if (!g_ptr_array_find (b, a->pdata[i], NULL)) + { + return FALSE; + } + } + + return TRUE; +} + +static void +set_true (gboolean *data) +{ + *data = TRUE; +} + +static void +collect_renamed_files (NautilusFile *file, + GFile *result_location, + GError *error, + gpointer callback_data) +{ + GPtrArray *renamed_files_arr = callback_data; + + g_ptr_array_add (renamed_files_arr, nautilus_file_ref (file)); +} + +static void +collect_changed_files (NautilusFilesView *view, + NautilusFile *file, + NautilusDirectory *directory, + GPtrArray *data_files) +{ + /* Ignore duplicate change emissions originating from nautilus-directory */ + if (!g_ptr_array_find (data_files, file, NULL)) + { + g_ptr_array_add (data_files, nautilus_file_ref (file)); + } +} + +static void +test_rename_files (void) +{ + g_autoptr (NautilusWindowSlot) slot = g_object_ref_sink (nautilus_window_slot_new (NAUTILUS_MODE_BROWSE)); + g_autoptr (NautilusFilesView) files_view = nautilus_files_view_new (NAUTILUS_VIEW_GRID_ID, slot); + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (files_view); + g_autoptr (GFile) tmp_location = g_file_new_for_path (test_get_tmp_dir ()); + const guint file_count = 10, renamed_file_count = 5; + g_autoptr (GPtrArray) file_arr = g_ptr_array_new_full (file_count, + (GDestroyNotify) nautilus_file_unref); + g_autoptr (GPtrArray) renamed_files_arr = g_ptr_array_new_full (renamed_file_count, + (GDestroyNotify) nautilus_file_unref); + g_autoptr (GPtrArray) callback_arr = g_ptr_array_new_full (renamed_file_count, + (GDestroyNotify) nautilus_file_unref); + gboolean added_files = FALSE, removed_files = FALSE; + + /* Create the files before loading the view and keep them in an array. */ + for (guint i = 0; i < file_count; i++) + { + g_autofree gchar *file_name = g_strdup_printf ("test_file_%i", i); + g_autoptr (GFile) file = g_file_get_child (tmp_location, file_name); + g_autoptr (GFileOutputStream) out = g_file_create (file, G_FILE_CREATE_NONE, NULL, NULL); + + g_assert_nonnull (out); + g_ptr_array_add (file_arr, nautilus_file_get (file)); + } + + nautilus_view_set_location (NAUTILUS_VIEW (files_view), tmp_location); + ITER_CONTEXT_WHILE (nautilus_files_view_get_loading (files_view)); + + g_assert_cmpint (g_list_model_get_n_items (G_LIST_MODEL (priv->model)), ==, file_count); + + /* Rename only some of the files and verify that they are emmited, but + * assert that no files are "added" or "removed". */ + g_signal_connect_swapped (files_view, "add-files", G_CALLBACK (set_true), &added_files); + g_signal_connect_swapped (files_view, "remove-file", G_CALLBACK (set_true), &removed_files); + g_signal_connect (files_view, "file-changed", + G_CALLBACK (collect_changed_files), callback_arr); + + for (guint i = 0; i < renamed_file_count; i++) + { + NautilusFile *file = file_arr->pdata[i]; + g_autoptr (GFile) location = nautilus_file_get_location (file); + g_autofree gchar *file_name = g_strdup_printf ("test_file_%i.txt", i); + nautilus_file_rename (file, file_name, collect_renamed_files, renamed_files_arr); + } + + ITER_CONTEXT_WHILE (callback_arr->len != renamed_file_count && + renamed_files_arr->len != renamed_file_count && + !ptr_arrays_equal_unordered (callback_arr, renamed_files_arr)); + + g_assert_cmpint (g_list_model_get_n_items (G_LIST_MODEL (priv->model)), ==, file_count); + g_assert_false (added_files); + g_assert_false (removed_files); + + /* Both renamed and non-renamed nautilus file pointers from before renaming + * should still point to the same files in the view. Renaming should not + * make the old pointers invalid since we're using nautilus_file_rename(). */ + for (guint i = 0; i < file_arr->len; i++) + { + NautilusFile *file = file_arr->pdata[i]; + NautilusViewItem *item = nautilus_view_model_get_item_for_file (priv->model, file); + + g_assert_nonnull (item); + } + + test_clear_tmp_dir (); +} + +static void +collect_removed_files_cb (NautilusFilesView *view, + GList *removed_files, + NautilusDirectory *directory, + GPtrArray *data_files) +{ + for (GList *link = removed_files; link != NULL; link = link->next) + { + g_ptr_array_add (data_files, nautilus_file_ref (NAUTILUS_FILE (link->data))); + } +} + +static void +test_remove_files (void) +{ + g_autoptr (NautilusWindowSlot) slot = g_object_ref_sink (nautilus_window_slot_new (NAUTILUS_MODE_BROWSE)); + g_autoptr (NautilusFilesView) files_view = nautilus_files_view_new (NAUTILUS_VIEW_GRID_ID, slot); + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (files_view); + g_autoptr (GFile) tmp_location = g_file_new_for_path (test_get_tmp_dir ()); + g_autofree gchar *uri = g_file_get_uri (tmp_location); + const guint file_count = 10, deleted_file_count = 5; + g_autoptr (GPtrArray) file_arr = g_ptr_array_new_full (file_count, + (GDestroyNotify) nautilus_file_unref); + g_autoptr (GPtrArray) deleted_files_arr = g_ptr_array_new (); + g_autoptr (GPtrArray) callback_arr = g_ptr_array_new_full (file_count, + (GDestroyNotify) nautilus_file_unref); + + /* Create the files before loading the view and keep them in an array. */ + for (guint i = 0; i < file_count; i++) + { + g_autofree gchar *file_name = g_strdup_printf ("test_file_%i", i); + g_autoptr (GFile) file = g_file_get_child (tmp_location, file_name); + g_autoptr (GFileOutputStream) out = g_file_create (file, G_FILE_CREATE_NONE, NULL, NULL); + + g_assert_nonnull (out); + g_ptr_array_add (file_arr, nautilus_file_get (file)); + } + + nautilus_view_set_location (NAUTILUS_VIEW (files_view), tmp_location); + + ITER_CONTEXT_WHILE (nautilus_files_view_get_loading (files_view)); + + g_assert_true (g_file_equal (tmp_location, + nautilus_view_get_location (NAUTILUS_VIEW (files_view)))); + g_assert_cmpint (g_list_model_get_n_items (G_LIST_MODEL (priv->model)), ==, file_count); + + + /* Delete only some of the files and verify that they are emmited */ + g_signal_connect (files_view, "remove-file", + G_CALLBACK (collect_removed_files_cb), callback_arr); + + for (guint i = 0; i < deleted_file_count; i++) + { + g_autoptr (GFile) location = nautilus_file_get_location (file_arr->pdata[i]); + g_autoptr (GError) error = NULL; + g_file_delete (location, NULL, &error); + + g_assert_null (error); + g_ptr_array_add (deleted_files_arr, file_arr->pdata[i]); + } + + ITER_CONTEXT_WHILE (!ptr_arrays_equal_unordered (deleted_files_arr, callback_arr)); + + g_assert_cmpint (g_list_model_get_n_items (G_LIST_MODEL (priv->model)), + ==, + file_count - deleted_file_count); + + for (guint i = 0; i < file_arr->len; i++) + { + NautilusFile *file = file_arr->pdata[i]; + NautilusViewItem *item = nautilus_view_model_get_item_for_file (priv->model, file); + + if (g_ptr_array_find (deleted_files_arr, file, NULL)) + { + g_assert_null (item); + } + else + { + g_assert_nonnull (item); + } + } + + test_clear_tmp_dir (); +} + +static void +collect_added_files_cb (NautilusFilesView *view, + GList *added_files, + GPtrArray *data_files) +{ + for (GList *link = added_files; link != NULL; link = link->next) + { + g_ptr_array_add (data_files, nautilus_file_ref (NAUTILUS_FILE (link->data))); + } +} + +static void +test_add_files (void) +{ + g_autoptr (NautilusWindowSlot) slot = g_object_ref_sink (nautilus_window_slot_new (NAUTILUS_MODE_BROWSE)); + g_autoptr (NautilusFilesView) files_view = nautilus_files_view_new (NAUTILUS_VIEW_GRID_ID, slot); + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (files_view); + g_autoptr (GFile) tmp_location = g_file_new_for_path (test_get_tmp_dir ()); + g_autofree gchar *uri = g_file_get_uri (tmp_location); + const guint file_count = 10; + g_autoptr (GPtrArray) file_arr = g_ptr_array_new_full (file_count, + (GDestroyNotify) nautilus_file_unref); + g_autoptr (GPtrArray) callback_arr = g_ptr_array_new_full (file_count, + (GDestroyNotify) nautilus_file_unref); + + nautilus_view_set_location (NAUTILUS_VIEW (files_view), tmp_location); + + ITER_CONTEXT_WHILE (nautilus_files_view_get_loading (files_view)); + + g_assert_true (g_file_equal (tmp_location, + nautilus_view_get_location (NAUTILUS_VIEW (files_view)))); + g_assert_cmpint (g_list_model_get_n_items (G_LIST_MODEL (priv->model)), ==, 0); + + /* Keep a list of NautilusFiles */ + for (guint i = 0; i < file_count; i++) + { + g_autofree gchar *file_name = g_strdup_printf ("test_file_%i", i); + g_autoptr (GFile) location = g_file_get_child (tmp_location, file_name); + + g_ptr_array_add (file_arr, nautilus_file_get (location)); + } + + /* Create files and verify that the view added the correct files */ + g_signal_connect (files_view, "add-files", G_CALLBACK (collect_added_files_cb), callback_arr); + + for (guint i = 0; i < file_arr->len; i++) + { + g_autoptr (GFile) location = nautilus_file_get_location (file_arr->pdata[i]); + g_autoptr (GFileOutputStream) out = g_file_create (location, + G_FILE_CREATE_NONE, NULL, NULL); + + g_assert_nonnull (out); + } + + ITER_CONTEXT_WHILE (!ptr_arrays_equal_unordered (file_arr, callback_arr)); + + g_assert_cmpint (g_list_model_get_n_items (G_LIST_MODEL (priv->model)), ==, file_count); + + for (guint i = 0; i < file_arr->len; i++) + { + NautilusFile *file = file_arr->pdata[i]; + + g_assert_nonnull (nautilus_view_model_get_item_for_file (priv->model, file)); + } + + test_clear_tmp_dir (); +} + +static void +test_load_dir (void) +{ + g_autoptr (NautilusWindowSlot) slot = g_object_ref_sink (nautilus_window_slot_new (NAUTILUS_MODE_BROWSE)); + g_autoptr (NautilusFilesView) files_view = nautilus_files_view_new (NAUTILUS_VIEW_GRID_ID, slot); + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (files_view); + g_autoptr (GFile) tmp_location = g_file_new_for_path (test_get_tmp_dir ()); + g_autofree gchar *uri = g_file_get_uri (tmp_location); + gboolean loading_started = FALSE, loading_ended = FALSE; + const guint file_count = 10; + + create_multiple_files ("view_test", file_count); + + /* Verify loading signals and location */ + nautilus_view_set_location (NAUTILUS_VIEW (files_view), tmp_location); + g_signal_connect_swapped (files_view, "begin-loading", G_CALLBACK (set_true), &loading_started); + g_signal_connect_swapped (files_view, "end-loading", G_CALLBACK (set_true), &loading_ended); + + g_assert_true (nautilus_files_view_get_loading (files_view)); + + ITER_CONTEXT_WHILE (nautilus_files_view_get_loading (files_view)); + + g_autofree gchar *view_uri = nautilus_files_view_get_uri (files_view); + + g_assert_true (g_file_equal (tmp_location, + nautilus_view_get_location (NAUTILUS_VIEW (files_view)))); + g_assert_cmpstr (view_uri, ==, uri); + g_assert_true (loading_started); + g_assert_true (loading_ended); + + /* Verify that files exist exist in the model */ + g_autoptr (GFileEnumerator) enumerator; + g_autoptr (GError) error = NULL; + GFile *child; + guint counter = 0; + + enumerator = g_file_enumerate_children (tmp_location, + G_FILE_ATTRIBUTE_STANDARD_NAME, + G_TYPE_FILE_QUERY_INFO_FLAGS, + NULL, + &error); + g_assert_no_error (error); + + /* Account for the extra folder */ + g_assert_cmpint (g_list_model_get_n_items (G_LIST_MODEL (priv->model)), ==, file_count + 1); + while (g_file_enumerator_iterate (enumerator, NULL, &child, NULL, NULL) && child != NULL) + { + g_autoptr (NautilusFile) file = nautilus_file_get (child); + g_assert_nonnull (nautilus_view_model_get_item_for_file (priv->model, file)); + counter++; + } + g_assert_cmpint (counter, ==, file_count + 1); + + test_clear_tmp_dir (); +} + +int +main (int argc, + char *argv[]) +{ + g_autoptr (NautilusTagManager) tag_manager = NULL; + + gtk_test_init (&argc, &argv, NULL); + + nautilus_register_resource (); + nautilus_ensure_extension_points (); + nautilus_global_preferences_init (); + tag_manager = nautilus_tag_manager_new_dummy (); + test_init_config_dir (); + + g_autoptr (NautilusApplication) app = nautilus_application_new (); + + g_test_add_func ("/view/load_dir", + test_load_dir); + g_test_add_func ("/view/add_files", + test_add_files); + g_test_add_func ("/view/remove_files", + test_remove_files); + g_test_add_func ("/view/change_files/rename", + test_rename_files); + + return g_test_run (); +} diff --git a/test/automated/displayless/meson.build b/test/automated/displayless/meson.build index 0d18bf5134fbaeb386acef893dce655938a43b1f..4aaf36a9e15f8a6c0a9ef73ba18e798fd58295d5 100644 --- a/test/automated/displayless/meson.build +++ b/test/automated/displayless/meson.build @@ -54,7 +54,7 @@ tracker_tests = [ foreach t: tests test( t[0], - executable(t[0], t[1], files('test-utilities.c'), dependencies: libnautilus_dep), + executable(t[0], t[1], dependencies: [libnautilus_dep, libtestutils_dep]), env: [ test_env, 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), @@ -70,7 +70,7 @@ endforeach # Tests that read and write from the Tracker index are run using 'tracker-sandbox' # script to use a temporary instance of tracker-miner-fs instead of the session one. foreach t: tracker_tests - test_exe = executable(t[0], t[1], files('test-utilities.c'), dependencies: libnautilus_dep) + test_exe = executable(t[0], t[1], dependencies: [libnautilus_dep, libtestutils_dep]) test( t[0], tracker_sandbox, diff --git a/test/automated/meson.build b/test/automated/meson.build index a1122423a87f74d1acd1331ed9e214845d29fd9b..dd97bdc76e83d2d8385f2aebe2620be0ca1e5b9f 100644 --- a/test/automated/meson.build +++ b/test/automated/meson.build @@ -1,3 +1,19 @@ +test_utils = files('test-utilities.c') + +libtestutils = static_library( + 'testutils', + test_utils, + dependencies: libnautilus_dep, + include_directories: include_directories('.') +) + +libtestutils_dep = declare_dependency( + link_with: libtestutils, + include_directories: include_directories('.'), + dependencies: libnautilus_dep, + sources: resources +) + subdir('displayless') if get_option('tests') == 'all' subdir('display') diff --git a/test/automated/displayless/test-utilities.c b/test/automated/test-utilities.c similarity index 87% rename from test/automated/displayless/test-utilities.c rename to test/automated/test-utilities.c index f00359034fb04f28a81a891925605865eb279a0b..b04d28f90ce5e06083ea28e546292610e2faf3dc 100644 --- a/test/automated/displayless/test-utilities.c +++ b/test/automated/test-utilities.c @@ -23,6 +23,46 @@ test_clear_tmp_dir (void) } } +static gboolean config_dir_initialized = FALSE; + +void +test_init_config_dir (void) +{ + if (config_dir_initialized == FALSE) + { + /* Initialize bookmarks */ + g_autofree gchar *gtk3_dir = g_build_filename (g_get_user_config_dir (), + "gtk-3.0", + NULL); + g_autofree gchar *bookmarks_path = g_build_filename (gtk3_dir, + "bookmarks", + NULL); + g_autoptr (GFile) bookmarks_file = g_file_new_for_path (bookmarks_path); + g_autoptr (GError) error = NULL; + + if (g_mkdir_with_parents (gtk3_dir, 0700) == -1) + { + int saved_errno = errno; + + g_error ("Failed to create bookmarks folder %s: %s", + gtk3_dir, g_strerror (saved_errno)); + return; + } + + g_autoptr (GFileOutputStream) stream = g_file_replace (bookmarks_file, + NULL, + FALSE, + G_FILE_CREATE_REPLACE_DESTINATION, + NULL, &error); + g_assert_no_error (error); + g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, &error); + g_assert_no_error (error); + + g_debug ("Initialized config folder %s", g_get_user_config_dir ()); + config_dir_initialized = TRUE; + } +} + void empty_directory_by_prefix (GFile *parent, gchar *prefix) @@ -57,7 +97,7 @@ empty_directory_by_prefix (GFile *parent, } } -static void +void create_hierarchy_from_template (const GStrv hier, const gchar *substitution) { diff --git a/test/automated/displayless/test-utilities.h b/test/automated/test-utilities.h similarity index 76% rename from test/automated/displayless/test-utilities.h rename to test/automated/test-utilities.h index 3c1247814de8c9f4ca14945a3cdb2f4d5338a2ed..359aed4fb4a26cb2d37264a8f339f76e15529d18 100644 --- a/test/automated/displayless/test-utilities.h +++ b/test/automated/test-utilities.h @@ -10,12 +10,28 @@ #pragma once +#define MAX_CONTEXT_ITERATIONS 100 + +#define ITER_CONTEXT_WHILE(CONDITION) \ + for (guint i = 0; \ + i < MAX_CONTEXT_ITERATIONS && \ + CONDITION; \ + i++) \ + { \ + g_main_context_iteration (NULL, TRUE); \ + } + const gchar *test_get_tmp_dir (void); void test_clear_tmp_dir (void); +void test_init_config_dir (void); + void empty_directory_by_prefix (GFile *parent, gchar *prefix); +void create_hierarchy_from_template (const GStrv hier, + const gchar *substitution); + void create_search_file_hierarchy (gchar *search_engine); void delete_search_file_hierarchy (gchar *search_engine);