1
0
mirror of https://gitlab.gnome.org/GNOME/nautilus synced 2024-06-28 14:35:28 +00:00

Compare commits

...

13 Commits

Author SHA1 Message Date
Khalid Abu Shawarib
48b6653658 Merge branch 'wip/kabus/batch-rename-revamp' into 'main'
Batch rename rework

Closes #1443

See merge request GNOME/nautilus!1388
2024-06-24 21:26:25 +00:00
Martin
58450f8f22 Update Slovenian translation 2024-06-23 13:13:23 +00:00
Alexandre Franke
4fd70e94a0 progress-info: harmonize spelling 2024-06-22 09:54:06 +00:00
Khalid Abu Shawarib
8f98ecbb44 test/file: Add batch rename tests 2024-02-02 15:52:02 +03:00
Khalid Abu Shawarib
04ca495429 test-utilities: Write the file name when creating a hierarchy
This will be used to confirm the content after renaming operations.
2024-01-30 18:13:30 +03:00
Khalid Abu Shawarib
7b460d79ae test-utilities: Export hierarchy creation function 2024-01-30 18:13:30 +03:00
Khalid Abu Shawarib
3cd96ff99c file-undo-operation: Fix batch rename list leak 2024-01-30 18:13:30 +03:00
Khalid Abu Shawarib
9c1c0bc032 file: Don't update files in info query async callback
The function used to update NautilusFile internal information after
being renamed are called in an async, which causes havok since the
batch renaming requires strict ordering to not add a NautiluFile
to a NautilusDirectory that has the same name. Instead, collect the
files that are changed while renaming and invalidate them in idle
callbacks.
2024-01-30 18:13:30 +03:00
Khalid Abu Shawarib
c7c16d5ac2 file: Batch rename rework
Move conflict resolution to nautilus-file.

Use the following rough algorithm to resolve conflicts:
1- If file is the start of a chain, start processing it until the
end of the chain. If it's not the start of a chain jump to step 3.
The file is the begining of a chain if the targeted new name
doesn't conflict with another file to be renamed, but may conflict
with a name that is designated as stub.
2- Try to rename the file at the start of the chain, if can't be
renamed (due to a conflict or depending on a stub), mark it as a
stub. Remove the file from the chain and go to step 1 with the
next file in the chain.
3- Follow the chain until: you find the beginning on the of the
chain, or you reach the same file you started with. If you reached
the file you started with, then it's cycle. break the cycle with a
temporary name, then go to step 1 with the file at the start of
the chain.

Fixes https://gitlab.gnome.org/GNOME/nautilus/-/issues/1443
2024-01-30 18:13:26 +03:00
Khalid Abu Shawarib
4a10fbf791 file: Don't call callback on every invalid rename
This was an error in [1] in which it's calling the passed callback
for every renaming failure, which was meant only for when the
entire operation ends. It was copied by mistake from the code for
single file rename operation.

[1] be12a75100
2024-01-29 16:46:48 +03:00
Khalid Abu Shawarib
20281558de file: Don't skip the first file from operation progress monitoring 2024-01-29 16:46:48 +03:00
Khalid Abu Shawarib
9e5135057f file: Remove unncessary condition
This code was supposed to be removed in e70c6e46b7.
2024-01-29 16:46:48 +03:00
Khalid Abu Shawarib
b8881bb946 batch-rename-utilities: Remove reordering function
This function doesn't work when there are cycles. Remove it in
favor of a another solution.
2024-01-29 16:46:45 +03:00
10 changed files with 627 additions and 521 deletions

469
po/sl.po

File diff suppressed because it is too large Load Diff

View File

@ -422,9 +422,6 @@ static void
begin_batch_rename (NautilusBatchRenameDialog *dialog,
GList *new_names)
{
batch_rename_sort_lists_for_rename (&dialog->selection, &new_names, NULL, NULL, NULL, FALSE);
/* do the actual rename here */
nautilus_file_batch_rename (dialog->selection, new_names, NULL, NULL);
gtk_widget_set_cursor (GTK_WIDGET (dialog->window), NULL);

View File

@ -142,124 +142,6 @@ batch_rename_replace (gchar *string,
return new_string;
}
void
batch_rename_sort_lists_for_rename (GList **selection,
GList **new_names,
GList **old_names,
GList **new_files,
GList **old_files,
gboolean is_undo_redo)
{
GList *new_names_list;
GList *new_names_list2;
GList *files;
GList *files2;
GList *old_names_list = NULL;
GList *new_files_list = NULL;
GList *old_files_list = NULL;
GList *old_names_list2 = NULL;
GList *new_files_list2 = NULL;
GList *old_files_list2 = NULL;
GString *new_file_name;
GString *new_name;
GString *old_name;
GFile *new_file;
GFile *old_file;
NautilusFile *file;
gboolean order_changed = TRUE;
/* in the following case:
* file1 -> file2
* file2 -> file3
* file2 must be renamed first, so because of that, the list has to be reordered
*/
while (order_changed)
{
order_changed = FALSE;
if (is_undo_redo)
{
old_names_list = *old_names;
new_files_list = *new_files;
old_files_list = *old_files;
}
for (new_names_list = *new_names, files = *selection;
new_names_list != NULL && files != NULL;
new_names_list = new_names_list->next, files = files->next)
{
g_autoptr (NautilusFile) parent = NULL;
new_file_name = new_names_list->data;
parent = nautilus_file_get_parent (NAUTILUS_FILE (files->data));
if (is_undo_redo)
{
old_names_list2 = old_names_list;
new_files_list2 = new_files_list;
old_files_list2 = old_files_list;
}
for (files2 = files, new_names_list2 = new_names_list;
files2 != NULL && new_names_list2 != NULL;
files2 = files2->next, new_names_list2 = new_names_list2->next)
{
const char *file_name;
g_autoptr (NautilusFile) parent2 = NULL;
file_name = nautilus_file_get_name (NAUTILUS_FILE (files2->data));
new_name = new_names_list2->data;
parent2 = nautilus_file_get_parent (NAUTILUS_FILE (files2->data));
if (files2 != files && g_strcmp0 (file_name, new_file_name->str) == 0 &&
parent == parent2)
{
file = NAUTILUS_FILE (files2->data);
*selection = g_list_remove_link (*selection, files2);
*new_names = g_list_remove_link (*new_names, new_names_list2);
*selection = g_list_prepend (*selection, file);
*new_names = g_list_prepend (*new_names, new_name);
if (is_undo_redo)
{
old_name = old_names_list2->data;
new_file = new_files_list2->data;
old_file = old_files_list2->data;
*old_names = g_list_remove_link (*old_names, old_names_list2);
*new_files = g_list_remove_link (*new_files, new_files_list2);
*old_files = g_list_remove_link (*old_files, old_files_list2);
*old_names = g_list_prepend (*old_names, old_name);
*new_files = g_list_prepend (*new_files, new_file);
*old_files = g_list_prepend (*old_files, old_file);
}
order_changed = TRUE;
break;
}
if (is_undo_redo)
{
old_names_list2 = old_names_list2->next;
new_files_list2 = new_files_list2->next;
old_files_list2 = old_files_list2->next;
}
}
if (is_undo_redo)
{
old_names_list = old_names_list->next;
new_files_list = new_files_list->next;
old_files_list = old_files_list->next;
}
}
}
}
/* This function changes the background color of the replaced part of the name */
GString *
batch_rename_replace_label_text (const char *label,

View File

@ -61,10 +61,3 @@ GString* batch_rename_replace_label_text (const char *label,
const gchar *substr);
gchar* batch_rename_get_tag_text_representation (TagConstants tag_constants);
void batch_rename_sort_lists_for_rename (GList **selection,
GList **new_names,
GList **old_names,
GList **new_files,
GList **old_files,
gboolean is_undo_redo);

View File

@ -31,7 +31,6 @@
#include "nautilus-file.h"
#include "nautilus-file-undo-manager.h"
#include "nautilus-batch-rename-dialog.h"
#include "nautilus-batch-rename-utilities.h"
#include "nautilus-scheme.h"
#include "nautilus-tag-manager.h"
@ -1199,7 +1198,8 @@ batch_rename_redo_func (NautilusFileUndoInfo *info,
{
NautilusFileUndoInfoBatchRename *self = NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME (info);
GList *l, *files;
GList *l;
g_autolist (NautilusFile) files = NULL;
NautilusFile *file;
GFile *old_file;
@ -1215,13 +1215,6 @@ batch_rename_redo_func (NautilusFileUndoInfo *info,
files = g_list_reverse (files);
batch_rename_sort_lists_for_rename (&files,
&self->new_display_names,
&self->old_display_names,
&self->new_files,
&self->old_files,
TRUE);
nautilus_file_batch_rename (files, self->new_display_names, file_undo_info_operation_callback, self);
}
@ -1232,7 +1225,8 @@ batch_rename_undo_func (NautilusFileUndoInfo *info,
{
NautilusFileUndoInfoBatchRename *self = NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME (info);
GList *l, *files;
GList *l;
g_autolist (NautilusFile) files = NULL;
NautilusFile *file;
GFile *new_file;
@ -1248,13 +1242,6 @@ batch_rename_undo_func (NautilusFileUndoInfo *info,
files = g_list_reverse (files);
batch_rename_sort_lists_for_rename (&files,
&self->old_display_names,
&self->new_display_names,
&self->old_files,
&self->new_files,
TRUE);
nautilus_file_batch_rename (files, self->old_display_names, file_undo_info_operation_callback, self);
}

View File

@ -1581,8 +1581,6 @@ nautilus_file_poll_for_media (NautilusFile *file)
gboolean
nautilus_file_can_rename (NautilusFile *file)
{
gboolean can_rename;
g_return_val_if_fail (NAUTILUS_IS_FILE (file), FALSE);
/* Nonexistent files can't be renamed. */
@ -1602,13 +1600,6 @@ nautilus_file_can_rename (NautilusFile *file)
return FALSE;
}
can_rename = TRUE;
if (!can_rename)
{
return FALSE;
}
return file->details->can_rename;
}
@ -2019,96 +2010,29 @@ nautilus_file_rename_handle_file_gone (NautilusFile *file,
return FALSE;
}
typedef struct
{
NautilusFileOperation *op;
NautilusFile *file;
} BatchRenameData;
static void
batch_rename_get_info_callback (GObject *source_object,
GAsyncResult *res,
gpointer callback_data)
{
NautilusFileOperation *op;
NautilusDirectory *directory;
NautilusFile *existing_file;
char *old_uri;
char *new_uri;
const char *new_name;
GFileInfo *new_info;
GError *error;
BatchRenameData *data;
data = callback_data;
op = data->op;
op->file = data->file;
error = NULL;
new_info = g_file_query_info_finish (G_FILE (source_object), res, &error);
if (new_info != NULL)
{
old_uri = nautilus_file_get_uri (op->file);
new_name = g_file_info_get_name (new_info);
directory = op->file->details->directory;
/* If there was another file by the same name in this
* directory and it is not the same file that we are
* renaming, mark it gone.
*/
existing_file = nautilus_directory_find_file_by_name (directory, new_name);
if (existing_file != NULL && existing_file != op->file)
{
nautilus_file_mark_gone (existing_file);
nautilus_file_changed (existing_file);
}
update_info_and_name (op->file, new_info);
new_uri = nautilus_file_get_uri (op->file);
nautilus_directory_moved (old_uri, new_uri);
g_free (new_uri);
g_free (old_uri);
g_object_unref (new_info);
}
op->renamed_files++;
if (op->files == NULL ||
op->renamed_files + op->skipped_files == g_list_length (op->files))
{
nautilus_file_operation_complete (op, NULL, error);
}
g_free (data);
if (error)
{
g_error_free (error);
}
}
static void
real_batch_rename (GList *files,
GList *new_names,
NautilusFileOperationCallback callback,
gpointer callback_data)
{
GList *l1, *l2, *old_files, *new_files;
GList *l1, *l2;
g_autolist (GFile) old_files = NULL, new_files = NULL;
NautilusFileOperation *op;
GFile *location;
GString *new_name;
NautilusFile *file;
GError *error;
GFile *new_file;
BatchRenameData *data;
error = NULL;
old_files = NULL;
new_files = NULL;
gsize len = 0;
g_autoptr (GHashTable) staged_names = g_hash_table_new_full (NULL, NULL, NULL, g_free);
g_autoptr (GHashTable) staged_files = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
g_autoptr (GHashTable) stub_files = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, g_object_unref);
g_autoptr (GHashTable) unchained_files = g_hash_table_new_full (NULL, NULL,
NULL, g_object_unref);
g_autoptr (GHashTable) modified_files = g_hash_table_new (NULL, NULL);
GHashTableIter hash_iter;
GFile *key;
gchar *value;
GFile *fallback;
g_autoptr (GError) skip_error = NULL;
/* Set up a batch renaming operation. */
op = nautilus_file_operation_new (files->data, callback, callback_data);
@ -2116,88 +2040,239 @@ real_batch_rename (GList *files,
op->renamed_files = 0;
op->skipped_files = 0;
for (l1 = files->next; l1 != NULL; l1 = l1->next)
{
file = NAUTILUS_FILE (l1->data);
file->details->operations_in_progress = g_list_prepend (file->details->operations_in_progress,
op);
}
for (l1 = files, l2 = new_names; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next)
{
const char *new_file_name;
file = NAUTILUS_FILE (l1->data);
new_name = l2->data;
NautilusFile *file = NAUTILUS_FILE (l1->data);
GString *new_name = l2->data;
GFile *location = nautilus_file_get_location (file);
const char *new_file_name = nautilus_file_can_rename_file (file,
new_name->str,
NULL, NULL);
location = nautilus_file_get_location (file);
new_file_name = nautilus_file_can_rename_file (file,
new_name->str,
callback,
callback_data);
len++;
file->details->operations_in_progress = g_list_prepend (file->details->operations_in_progress,
op);
if (new_file_name == NULL)
{
op->skipped_files++;
name_is (file, new_name->str) ? op->renamed_files++ : op->skipped_files++;
g_hash_table_insert (stub_files, g_strdup (file->details->name), location);
continue;
}
g_assert (G_IS_FILE (location));
g_assert (g_hash_table_insert (staged_names, location, g_strdup (new_file_name)));
g_assert (g_hash_table_insert (staged_files, g_strdup (file->details->name), location));
}
/* Do the renaming. */
new_file = g_file_set_display_name (location,
new_file_name,
op->cancellable,
&error);
g_hash_table_iter_init (&hash_iter, staged_names);
g_hash_table_iter_next (&hash_iter, (gpointer *) &key, (gpointer *) &value);
while (g_hash_table_size (staged_names) > 0)
{
GFile *chain_file = key;
GFile *conflicting_staged_file = g_hash_table_lookup (staged_files, value);
gboolean is_chain_start = TRUE;
data = g_new0 (BatchRenameData, 1);
data->op = op;
data->file = file;
g_file_query_info_async (new_file,
NAUTILUS_FILE_DEFAULT_ATTRIBUTES,
0,
G_PRIORITY_DEFAULT,
op->cancellable,
batch_rename_get_info_callback,
data);
if (error != NULL)
if (conflicting_staged_file != NULL)
{
g_warning ("Batch rename for file \"%s\" failed", nautilus_file_get_name (file));
g_error_free (error);
error = NULL;
g_autoptr (GFile) conflicting_staged_file_parent = g_file_get_parent (conflicting_staged_file);
op->skipped_files++;
is_chain_start = !g_file_has_parent (chain_file, conflicting_staged_file_parent);
}
if (is_chain_start)
{
/* Process the chain leading to this file */
gboolean skip_files = FALSE;
while (chain_file != NULL)
{
g_autofree gchar *chain_file_old_name = g_file_get_basename (chain_file);
gchar *chain_file_new_name = g_hash_table_lookup (staged_names, chain_file);
GFile *conflicting_stub_file = g_hash_table_lookup (stub_files,
chain_file_new_name);
if (!skip_files && conflicting_stub_file != NULL)
{
g_autoptr (GFile) conflicting_stub_file_parent = g_file_get_parent (conflicting_stub_file);
skip_files = g_file_has_parent (chain_file, conflicting_stub_file_parent);
}
/* Rename and skip the chain on error */
if (!skip_files)
{
g_autoptr (GError) error = NULL;
/* Do the renaming. */
g_autoptr (GFile) renamed_file = g_file_set_display_name (chain_file,
chain_file_new_name,
op->cancellable,
&error);
if (error != NULL)
{
g_warning ("Batch rename for file \"%s\" failed: %s",
chain_file_old_name, error->message);
/* We need to skip the rest of the files in the chain. */
skip_files = TRUE;
}
else
{
op->renamed_files++;
if (g_hash_table_steal_extended (unchained_files, chain_file,
NULL, (gpointer *) &fallback))
{
g_hash_table_add (modified_files, fallback);
old_files = g_list_append (old_files, fallback);
}
else
{
g_hash_table_add (modified_files, chain_file);
old_files = g_list_append (old_files, g_object_ref (chain_file));
}
g_hash_table_add (modified_files, renamed_file);
new_files = g_list_append (new_files, g_steal_pointer (&renamed_file));
}
}
g_hash_table_remove (staged_names, chain_file);
g_hash_table_remove (staged_files, chain_file_old_name);
if (skip_files)
{
op->skipped_files++;
g_hash_table_insert (stub_files, g_strdup (chain_file_old_name),
g_steal_pointer (&chain_file));
}
g_set_object (&chain_file, g_hash_table_lookup (staged_files, chain_file_old_name));
}
g_hash_table_iter_init (&hash_iter, staged_names);
g_hash_table_iter_next (&hash_iter, (gpointer *) &key, (gpointer *) &value);
}
else
{
old_files = g_list_append (old_files, location);
new_files = g_list_append (new_files, new_file);
/* Iterate to find out if it's a chain or a cycle. */
GFile *iter_start_file = NULL;
while (chain_file != NULL)
{
gchar *chain_file_new_name = g_hash_table_lookup (staged_names, chain_file);
GFile *prev_chain_file = g_hash_table_lookup (staged_files, chain_file_new_name);
gboolean reached_chain_start = TRUE;
if (prev_chain_file != NULL)
{
g_autoptr (GFile) prev_chain_file_parent = g_file_get_parent (prev_chain_file);
reached_chain_start = !g_file_has_parent (chain_file, prev_chain_file_parent);
}
if (reached_chain_start)
{
key = chain_file;
value = chain_file_new_name;
break;
}
if (iter_start_file == chain_file)
{
/* We found a cycle, break it with a temporary name. */
g_autofree gchar *random_string = g_uuid_string_random ();
gchar *prev_chain_file_old_name = chain_file_new_name;
g_autofree gchar *prev_chain_file_new_name = NULL;
GFile *renamed_file = g_file_set_display_name (prev_chain_file,
random_string,
op->cancellable,
NULL);
g_hash_table_remove (staged_files, prev_chain_file_old_name);
g_hash_table_steal_extended (staged_names, prev_chain_file,
NULL, (gpointer *) &prev_chain_file_new_name);
if (renamed_file != NULL)
{
g_hash_table_insert (staged_files, g_strdup (random_string), renamed_file);
g_hash_table_insert (staged_names,
renamed_file,
g_steal_pointer (&prev_chain_file_new_name));
g_hash_table_insert (unchained_files, renamed_file, prev_chain_file);
}
else
{
/* Unlikely to be a name conflict, stub the file. */
op->skipped_files++;
g_hash_table_insert (stub_files,
g_strdup (prev_chain_file_old_name), prev_chain_file);
}
key = chain_file;
value = chain_file_new_name;
break;
}
if (iter_start_file == NULL)
{
iter_start_file = chain_file;
}
chain_file = prev_chain_file;
}
}
}
/* Attempt to rename back the files that were renamed to temporary names
* but weren't restored. Don't handle any errors though, it's likely
* futile.
*/
g_hash_table_iter_init (&hash_iter, unchained_files);
while (g_hash_table_iter_next (&hash_iter, (gpointer *) &key, (gpointer *) &fallback))
{
g_file_move (key, fallback, G_FILE_COPY_NONE, NULL, NULL, NULL, NULL);
}
g_hash_table_iter_init (&hash_iter, modified_files);
while (g_hash_table_iter_next (&hash_iter, (gpointer *) &key, NULL))
{
NautilusFile *file = nautilus_file_get_existing (key);
if (file != NULL)
{
nautilus_file_invalidate_attributes (file, NAUTILUS_FILE_ATTRIBUTE_INFO);
}
}
/* Tell the undo manager a batch rename is taking place if at least
* a file has been renamed*/
if (!nautilus_file_undo_manager_is_operating () && op->skipped_files != g_list_length (files))
if (!nautilus_file_undo_manager_is_operating () && op->skipped_files != len)
{
op->undo_info = nautilus_file_undo_info_batch_rename_new (g_list_length (new_files));
nautilus_file_undo_info_batch_rename_set_data_pre (NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME (op->undo_info),
old_files);
g_steal_pointer (&old_files));
nautilus_file_undo_info_batch_rename_set_data_post (NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME (op->undo_info),
new_files);
g_steal_pointer (&new_files));
nautilus_file_undo_manager_set_action (op->undo_info);
}
if (op->skipped_files == g_list_length (files))
if (op->skipped_files > 1)
{
nautilus_file_operation_complete (op, NULL, error);
skip_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
"Skipped %d file(s)", op->skipped_files);
}
nautilus_file_operation_complete (op, NULL, skip_error);
}
void

View File

@ -372,7 +372,7 @@ on_canceled (GCancellable *cancellable,
NautilusProgressInfo *info)
{
G_LOCK (progress_info);
set_details (info, _("Canceled"));
set_details (info, _("Cancelled"));
info->cancel_at_idle = TRUE;
g_timer_stop (info->progress_timer);
queue_idle (info, TRUE);

View File

@ -4,6 +4,7 @@
#include <nautilus-file.h>
#include <nautilus-file-private.h>
#include <nautilus-file-utilities.h>
#include <test-utilities.h>
static void
@ -87,10 +88,167 @@ test_file_sort_with_self (void)
g_assert_cmpint (order, ==, 0);
}
typedef struct
{
const gsize len;
GStrv expected_names;
GStrv expected_content;
gboolean *success;
} NautilusFileBatchRenameTestData;
static void
batch_rename_callback (NautilusFile *file,
GFile *result_location,
GError *error,
gpointer callback_data)
{
NautilusFileBatchRenameTestData *data = callback_data;
g_autoptr (GFile) root = g_file_new_for_path (test_get_tmp_dir ());
g_autoptr (GStrvBuilder) name_builder = g_strv_builder_new ();
g_autoptr (GStrvBuilder) content_builder = g_strv_builder_new ();
g_auto (GStrv) result_names = NULL, result_content = NULL;
g_assert_no_error (error);
g_assert_cmpint (data->len, ==, g_strv_length (data->expected_names));
g_assert_cmpint (data->len, ==, g_strv_length (data->expected_content));
for (guint i = 0; i < data->len; i++)
{
g_autoptr (GFile) location = g_file_get_child (root, data->expected_names[i]);
g_autoptr (GFileInfo) info = g_file_query_info (location, NAUTILUS_FILE_DEFAULT_ATTRIBUTES,
G_FILE_QUERY_INFO_NONE, NULL, NULL);
g_autoptr (GFileInputStream) stream = g_file_read (location, NULL, NULL);
gchar content[1024];
g_assert_nonnull (stream);
g_assert_true (g_input_stream_read_all (G_INPUT_STREAM (stream), content, sizeof (content),
NULL, NULL, NULL));
g_assert_true (g_input_stream_close (G_INPUT_STREAM (stream), NULL, NULL));
g_strv_builder_add (name_builder, g_file_info_get_display_name (info));
g_strv_builder_add (content_builder, content);
}
result_names = g_strv_builder_end (name_builder);
result_content = g_strv_builder_end (content_builder);
for (guint i = 0; i < data->len; i++)
{
g_assert_cmpstr (result_names[i], ==, data->expected_names[i]);
g_assert_cmpstr (result_content[i], ==, data->expected_content[i]);
}
*data->success = TRUE;
}
static void
batch_rename_test (const GStrv original_names,
const GStrv expected_names)
{
g_autoptr (GFile) root = g_file_new_for_path (test_get_tmp_dir ());
g_autolist (NautilusFile) files = NULL;
g_autolist (GString) new_names = NULL;
const gsize len = g_strv_length (expected_names);
gboolean success = FALSE;
NautilusFileBatchRenameTestData data = { len, expected_names, original_names, &success };
create_hierarchy_from_template (original_names, "");
for (gint i = len - 1; i >= 0; i--)
{
g_autoptr (GFile) location = g_file_get_child (root, original_names[i]);
NautilusFile *file = nautilus_file_get (location);
GString *new_name = g_string_new (expected_names[i]);
files = g_list_prepend (files, file);
new_names = g_list_prepend (new_names, new_name);
}
nautilus_file_batch_rename (files, new_names, batch_rename_callback, &data);
g_assert_true (success);
/* Test undo by changing the expected names */
data.expected_names = original_names;
success = FALSE;
test_operation_undo ();
batch_rename_callback (NULL, NULL, NULL, &data);
g_assert_true (success);
test_clear_tmp_dir ();
}
static void
test_file_batch_rename_cycles (void)
{
char *test_cases[][2][10] =
{
/* Small cycle */
{{"file_1", "file_2", NULL},
{"file_2", "file_1", NULL}},
/* Medium cycle */
{{"file_1", "file_2", "file_3", "file_4", "file_5", "file_6", "file_7", "file_8", "file_9", NULL},
{"file_9", "file_1", "file_2", "file_3", "file_4", "file_5", "file_6", "file_7", "file_8", NULL}},
/* Multi-cycle */
{{"file_1", "file_2", "file_3", "file_4", "file_5", "file_6", "file_7", "file_8", NULL},
{"file_8", "file_3", "file_4", "file_5", "file_6", "file_7", "file_2", "file_1", NULL}},
};
g_test_bug ("https://gitlab.gnome.org/GNOME/nautilus/-/issues/1443");
for (guint i = 0; i < G_N_ELEMENTS (test_cases); i++)
{
batch_rename_test (test_cases[i][0], test_cases[i][1]);
}
}
static void
test_file_batch_rename_chains (void)
{
char *test_cases[][2][10] =
{
/* Medium chain */
{{"file_1", "file_2", "file_3", "file_4", "file_5", "file_6", "file_7", "file_8", "file_9", NULL},
{"file_2", "file_3", "file_4", "file_5", "file_6", "file_7", "file_8", "file_9", "file_10", NULL}},
};
for (guint i = 0; i < G_N_ELEMENTS (test_cases); i++)
{
batch_rename_test (test_cases[i][0], test_cases[i][1]);
}
}
static void
test_file_batch_rename_replace (void)
{
char *test_cases[][2][10] =
{
/* File Extension replacement */
{{
"file_1.jpg", "file_2.jpeg", "file_3.gif", "file_4.png", "file_5.webm",
"file_6.avif", "file_7.jxl", "file_8.jpeg", "file_9.bmp", NULL
},
{
"file_1.jpg", "file_2.jpg", "file_3.gif", "file_4.png", "file_5.webm",
"file_6.avif", "file_7.jxl", "file_8.jpg", "file_9.bmp", NULL
}},
};
for (guint i = 0; i < G_N_ELEMENTS (test_cases); i++)
{
batch_rename_test (test_cases[i][0], test_cases[i][1]);
}
}
int
main (int argc,
char *argv[])
{
g_autoptr (NautilusFileUndoManager) undo_manager = nautilus_file_undo_manager_new ();
g_test_init (&argc, &argv, NULL);
g_test_set_nonfatal_assertions ();
nautilus_ensure_extension_points ();
@ -107,6 +265,12 @@ main (int argc,
test_file_sort_order);
g_test_add_func ("/file-sort/with-self",
test_file_sort_with_self);
g_test_add_func ("/file-batch-rename/cycles",
test_file_batch_rename_cycles);
g_test_add_func ("/file-batch-rename/chains",
test_file_batch_rename_chains);
g_test_add_func ("/file-batch-rename/replace",
test_file_batch_rename_replace);
return g_test_run ();
}

View File

@ -57,7 +57,7 @@ empty_directory_by_prefix (GFile *parent,
}
}
static void
void
create_hierarchy_from_template (const GStrv hier,
const gchar *substitution)
{
@ -82,6 +82,10 @@ create_hierarchy_from_template (const GStrv hier,
{
g_autoptr (GFileOutputStream) stream = g_file_create (file, G_FILE_CREATE_NONE,
NULL, NULL);
g_autofree gchar *name = g_file_get_basename (file);
g_output_stream_write_all (G_OUTPUT_STREAM (stream), name, strlen (name) + 1,
NULL, NULL, NULL);
}
}
}

View File

@ -16,6 +16,9 @@ void test_clear_tmp_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);