diff --git a/README.md b/README.md index bc14928..4464e42 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Viewnior - Fast and elegant image viewer -This is Viewnior, an image viewer program. Created to be simple, fast and elegant. It's minimalistic interface provides more screenspace for your images. Among its features are: +This is Viewnior, an image viewer program. Created to be simple, fast and elegant. Its minimalistic interface provides more screenspace for your images. Among its features are: * Fullscreen & Slideshow * Rotate, flip, crop, save, delete images @@ -11,8 +11,10 @@ This is Viewnior, an image viewer program. Created to be simple, fast and elegan * EXIF and IPTC metadata * Simple interface * Configurable mouse actions + * Recursively opens images in subfolders + * Updates when images are added or removed from folder -Most of the Viewnior's sources handling image viewing are adopted from the GtkImageView library by Björn Lindqvist. The files were cleaned up and modified, so that unused functionalities were removed (GtkImageToolSelector, GtkImageToolPainter, GtkZooms). Prefixes were changed from gtk_ to uni_ for clarity. +Most of the Viewnior's sources handling image viewing are adopted from the GtkImageView library by Björn Lindqvist. The files were cleaned up and modified, so that unused functionality were removed (GtkImageToolSelector, GtkImageToolPainter, GtkZooms). Prefixes were changed from gtk_ to uni_ for clarity. ## Requirements @@ -34,4 +36,4 @@ This program is released under the terms of the [GNU General Public License](htt object-rotate-left.png, object-rotate-right.png are taken from Elementary icon theme by ~DanRabbit (under GPL). object-flip-horizontal.png, object-flip-vertical.png are taken from Gnome icon theme (under GPL). -*Last Edited - 28 April 2018* +*Last Edited - 21 August 2018* diff --git a/TODO b/TODO index dbae820..05dd5a4 100644 --- a/TODO +++ b/TODO @@ -3,5 +3,4 @@ * GTK+3 migration * Add documentation - * Add file monitoring * Add lossless JPG rotation diff --git a/meson.build b/meson.build index 204221f..82f9a31 100644 --- a/meson.build +++ b/meson.build @@ -52,5 +52,6 @@ subdir('po') subdir('data') subdir('man') subdir('src') +subdir('tests') meson.add_install_script('meson_post_install.py') \ No newline at end of file diff --git a/src/main.c b/src/main.c index 980ac83..b976d66 100755 --- a/src/main.c +++ b/src/main.c @@ -23,6 +23,7 @@ #include #include #include "config.h" +#include "vnr-tree.h" #include "vnr-window.h" #include "vnr-message-area.h" #include "vnr-file.h" @@ -34,6 +35,7 @@ static gchar **files = NULL; //array of files specified to be opened static gboolean version = FALSE; static gboolean slideshow = FALSE; static gboolean fullscreen = FALSE; +static gboolean recursive = FALSE; /* List of option entries * The only option is for specifying file to be opened. */ @@ -42,18 +44,17 @@ static GOptionEntry opt_entries[] = { {"version", 0, 0, G_OPTION_ARG_NONE, &version, NULL, NULL}, {"slideshow", 0, 0, G_OPTION_ARG_NONE, &slideshow, NULL, NULL}, {"fullscreen", 0, 0, G_OPTION_ARG_NONE, &fullscreen, NULL, NULL}, + {"recursive", 0, 0, G_OPTION_ARG_NONE, &recursive, NULL, NULL}, {NULL} }; -int -main (int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { GError *error = NULL; GOptionContext *opt_context; GtkWindow *window; GSList *uri_list = NULL; - GList *file_list = NULL; + GNode *tree = NULL; bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR); @@ -65,15 +66,12 @@ main (int argc, char *argv[]) g_option_context_add_group (opt_context, gtk_get_option_group (TRUE)); g_option_context_parse (opt_context, &argc, &argv, &error); - if (error != NULL) - { + if(error != NULL) { printf ("%s\nRun 'viewnior --help' to see a full list of available command line options.\n", error->message); return 1; - } - else if(version) - { + } else if(version) { printf("%s\n", PACKAGE_STRING); return 0; } @@ -86,47 +84,36 @@ main (int argc, char *argv[]) uri_list = vnr_tools_get_list_from_array (files); - if(uri_list != NULL) - { - if (g_slist_length(uri_list) == 1) - { - vnr_file_load_single_uri (uri_list->data, &file_list, VNR_WINDOW(window)->prefs->show_hidden, &error); - } - else - { - vnr_file_load_uri_list (uri_list, &file_list, VNR_WINDOW(window)->prefs->show_hidden, &error); + if(uri_list != NULL) { + if(g_slist_length(uri_list) == 1) { + tree = create_tree_from_single_uri (uri_list->data, VNR_WINDOW(window)->prefs->show_hidden, recursive, tree_changed_callback, window, &error); + } else { + tree = create_tree_from_uri_list (uri_list, VNR_WINDOW(window)->prefs->show_hidden, recursive, tree_changed_callback, window, &error); } - if(error != NULL && file_list != NULL) - { + if(error != NULL && tree != NULL) { deny_slideshow(VNR_WINDOW(window)); vnr_message_area_show(VNR_MESSAGE_AREA (VNR_WINDOW(window)->msg_area), TRUE, error->message, TRUE); - vnr_window_set_list(VNR_WINDOW(window), file_list, TRUE); - } - else if(error != NULL) - { + vnr_window_set_tree(VNR_WINDOW(window), tree, TRUE); + } else if(error != NULL) { deny_slideshow(VNR_WINDOW(window)); vnr_message_area_show(VNR_MESSAGE_AREA (VNR_WINDOW(window)->msg_area), TRUE, error->message, TRUE); - } - else if(file_list == NULL) - { + } else if(tree == NULL) { deny_slideshow(VNR_WINDOW(window)); vnr_message_area_show(VNR_MESSAGE_AREA (VNR_WINDOW(window)->msg_area), TRUE, _("The given locations contain no images."), TRUE); - } - else - { - vnr_window_set_list(VNR_WINDOW(window), file_list, TRUE); + } else { + vnr_window_set_tree(VNR_WINDOW(window), tree, TRUE); } } - + VNR_WINDOW(window)->prefs->start_slideshow = slideshow; VNR_WINDOW(window)->prefs->start_fullscreen = fullscreen; - if ( VNR_WINDOW(window)->prefs->start_maximized ) { - gtk_window_maximize(window); + if(VNR_WINDOW(window)->prefs->start_maximized) { + gtk_window_maximize(window); } gtk_widget_show (GTK_WIDGET (window)); gtk_main (); diff --git a/src/meson.build b/src/meson.build index 0107157..eb574a3 100644 --- a/src/meson.build +++ b/src/meson.build @@ -12,6 +12,7 @@ viewnior_sources = [ 'uni-image-view.c', 'vnr-message-area.c', 'vnr-properties-dialog.c', + 'vnr-tree.c', 'vnr-file.c', 'uni-utils.c', 'vnr-prefs.c', diff --git a/src/vnr-callback-interface.h b/src/vnr-callback-interface.h new file mode 100644 index 0000000..9a33e49 --- /dev/null +++ b/src/vnr-callback-interface.h @@ -0,0 +1,60 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#ifndef __CALLBACK_INTERFACE_H__ +#define __CALLBACK_INTERFACE_H__ + +#include + +/** + * A callback function that will be called when a file or directory with + * a file monitor have reported a change, i.e. a file or directory has + * been added or removed. + * + * @deleted@ is TRUE if a file or directory has been deleted. If it was + * created, it will be FALSE. + * @path@ is the path to the file or directory that was affected. The + * memory will be freed after the call to callback. + * @changed_node@ is the subtree that was affected. If the file or + * directory was created, then it has been added to @root@ already. If + * it was deleted, @changed_node@ has been removed from @root@ and it + * along with its subnodes have been freed. The pointer may thus point + * to an invalid structure. + * @root@ is the root node of the tree that @changed_node@ is/was part + * of. + * @data@ is user provided data that will be sent back to the callback + * function unaltered. + */ +typedef void (*callback)(gboolean deleted, + char *path, + GNode *changed_node, + GNode *root, + gpointer data); + + +struct MonitoringData { + gboolean include_hidden; + gboolean include_dirs; + gboolean set_file_monitor_for_file; + GNode* tree; + callback cb; + gpointer cb_data; +}; + +#endif /* __CALLBACK_INTERFACE_H__ */ diff --git a/src/vnr-file.c b/src/vnr-file.c index 2649fdc..d8f694b 100644 --- a/src/vnr-file.c +++ b/src/vnr-file.c @@ -17,268 +17,74 @@ * along with Viewnior. If not, see . */ -#include -#include -#define _(String) gettext (String) - -#include -#include -#include #include "vnr-file.h" -#include "vnr-tools.h" - -G_DEFINE_TYPE (VnrFile, vnr_file, G_TYPE_OBJECT); - -GList * supported_mime_types; - -static gint -compare_files(VnrFile *file, char *uri) -{ - if(g_strcmp0(uri, file->path) == 0) - return 0; - else - return 1; -} - -/* Modified version of eog's eog_image_get_supported_mime_types */ -static GList * -vnr_file_get_supported_mime_types (void) -{ - GSList *format_list, *it; - - if (!supported_mime_types) { - format_list = gdk_pixbuf_get_formats (); - for (it = format_list; it != NULL; it = it->next) { - gchar **mime_types = gdk_pixbuf_format_get_mime_types((GdkPixbufFormat *) it->data); +#include - int i; - for (i = 0; mime_types[i] != NULL; i++) { - supported_mime_types = - g_list_prepend (supported_mime_types, - g_strdup (mime_types[i])); - } +#define UNUSED(x) (void)(x) - g_strfreev (mime_types); - } +G_DEFINE_TYPE (VnrFile, vnr_file, G_TYPE_OBJECT) - supported_mime_types = g_list_prepend(supported_mime_types, - "image/vnd.microsoft.icon"); - supported_mime_types = g_list_sort (supported_mime_types, - (GCompareFunc) compare_quarks); - - g_slist_free (format_list); - } - - return supported_mime_types; +static void vnr_file_class_init (VnrFileClass *klass) { + UNUSED(klass); } -static gboolean -vnr_file_is_supported_mime_type (const char *mime_type) -{ - GList *result; - GQuark quark; - - if (mime_type == NULL) { - return FALSE; - } - - supported_mime_types = vnr_file_get_supported_mime_types (); - - quark = g_quark_from_string (mime_type); - - result = g_list_find_custom (supported_mime_types, - GINT_TO_POINTER (quark), - (GCompareFunc) compare_quarks); - - return (result != NULL); -} - -static void -vnr_file_class_init (VnrFileClass * klass) -{ -} - -static void -vnr_file_init (VnrFile * file) -{ +static void vnr_file_init(VnrFile *file) { file->display_name = NULL; } -VnrFile * -vnr_file_new () -{ +VnrFile * vnr_file_new() { return VNR_FILE (g_object_new (VNR_TYPE_FILE, NULL)); } -static void -vnr_file_set_display_name(VnrFile *vnr_file, char *display_name) -{ +static void vnr_file_set_display_name(VnrFile *vnr_file, char *display_name) { vnr_file->display_name = g_strdup(display_name); - vnr_file->display_name_collate = g_utf8_collate_key_for_filename(display_name, -1); -} - - -static gint -vnr_file_list_compare(gconstpointer a, gconstpointer b, gpointer user_data){ - return g_strcmp0(VNR_FILE(a)->display_name_collate, - VNR_FILE(b)->display_name_collate); + vnr_file->display_name_collate = g_utf8_collate_key_for_filename(vnr_file->display_name, -1); } -static GList * -vnr_file_dir_content_to_list(gchar *path, gboolean sort, gboolean include_hidden) +static void vnr_file_set_file_info(VnrFile *vnrfile, + char *path, + char *display_name, + gboolean is_directory) { - GList *file_list = NULL; - GFile *file; - GFileEnumerator *f_enum ; - GFileInfo *file_info; - - file = g_file_new_for_path(path); - f_enum = g_file_enumerate_children(file, G_FILE_ATTRIBUTE_STANDARD_NAME"," - G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME"," - G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE"," - G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN, - G_FILE_QUERY_INFO_NONE, - NULL, NULL); - file_info = g_file_enumerator_next_file(f_enum,NULL,NULL); - - - while(file_info != NULL){ - VnrFile *vnr_file = vnr_file_new(); + vnrfile->path = g_strdup(path); + vnrfile->is_directory = is_directory; + vnr_file_set_display_name(vnrfile, display_name); - const char *mimetype =g_file_info_get_content_type(file_info); - - if(vnr_file_is_supported_mime_type(mimetype) && (include_hidden || !g_file_info_get_is_hidden (file_info)) ){ - vnr_file_set_display_name(vnr_file, (char*)g_file_info_get_display_name (file_info)); - - vnr_file->path =g_strjoin(G_DIR_SEPARATOR_S, path, - vnr_file->display_name, NULL); - - file_list = g_list_prepend(file_list, vnr_file); - } - - g_object_unref(file_info); - file_info = g_file_enumerator_next_file(f_enum,NULL,NULL); - } - - g_object_unref (file); - g_file_enumerator_close (f_enum, NULL, NULL); - g_object_unref (f_enum); - - if(sort) - file_list = g_list_sort_with_data(file_list, - vnr_file_list_compare, NULL); - - return file_list; } - -void -vnr_file_load_single_uri(char *p_path, GList **file_list, gboolean include_hidden, GError **error) +VnrFile* vnr_file_create_new(gchar *path, + char *display_name, + gboolean is_directory) { - GFile *file; - GFileInfo *fileinfo; - GFileType filetype; - - file = g_file_new_for_path(p_path); - fileinfo = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE"," - G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, - 0, NULL, error); + VnrFile *vnrfile = vnr_file_new(); + vnr_file_set_file_info(vnrfile, path, display_name, is_directory); + return vnrfile; +} - if (fileinfo == NULL) +void vnr_file_destroy_data(VnrFile *vnrfile) { + if(vnrfile == NULL) { return; - - filetype = g_file_info_get_file_type(fileinfo); - - if (filetype == G_FILE_TYPE_DIRECTORY) - { - *file_list = vnr_file_dir_content_to_list(p_path, TRUE, include_hidden); } - else - { - GFile *parent; - GList *current_position; - - parent = g_file_get_parent(file); - *file_list = vnr_file_dir_content_to_list(g_file_get_path(parent), TRUE, include_hidden); - - g_object_unref(parent); - - current_position = g_list_find_custom(*file_list, p_path, - (GCompareFunc)compare_files); - - if(current_position != NULL) - *file_list = current_position; - else if(*file_list == NULL) - return; - else - { - *error = g_error_new(1, 0, - _("Couldn't recognise the image file\n" - "format for file '%s'"), - g_file_info_get_display_name (fileinfo)); - } + if(vnrfile->monitoring_data != NULL) { + free(vnrfile->monitoring_data); + } + if(vnrfile->monitor != NULL) { + g_file_monitor_cancel(vnrfile->monitor); + g_object_unref(vnrfile->monitor); } - g_object_unref (file); - g_object_unref(fileinfo); + g_free(vnrfile->path); + g_free(vnrfile->display_name); + g_free((gpointer) vnrfile->display_name_collate); + g_object_unref(vnrfile); } -void -vnr_file_load_uri_list (GSList *uri_list, GList **file_list, gboolean include_hidden, GError **error) -{ - GFileType filetype; - gchar *p_path; - - while(uri_list != NULL) - { - p_path = uri_list->data; - GFile *file = g_file_new_for_path(p_path); - GFileInfo *fileinfo = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE"," - G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME"," - G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE"," - G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN, - 0, NULL, error); - - if (fileinfo == NULL) - { - g_clear_error (error); - g_object_unref (file); - - uri_list = g_slist_next(uri_list); - continue; - } - - filetype = g_file_info_get_file_type(fileinfo); - - if (filetype == G_FILE_TYPE_DIRECTORY) - { - *file_list = g_list_concat (*file_list, vnr_file_dir_content_to_list(p_path, FALSE, include_hidden)); - } - else - { - VnrFile *new_vnrfile; - const char *mimetype; - - new_vnrfile = vnr_file_new(); - - mimetype = g_file_info_get_content_type(fileinfo); - - if(vnr_file_is_supported_mime_type(mimetype) && (include_hidden || !g_file_info_get_is_hidden (fileinfo)) ) - { - vnr_file_set_display_name(new_vnrfile, (char*)g_file_info_get_display_name (fileinfo)); - - new_vnrfile->path = p_path; - - *file_list = g_list_prepend(*file_list, new_vnrfile); - } - } - g_object_unref (file); - g_object_unref (fileinfo); - - uri_list = g_slist_next(uri_list); - } +gboolean vnr_file_is_directory(VnrFile* vnrfile) { + return vnrfile != NULL && vnrfile->is_directory; +} - *file_list = g_list_sort_with_data(*file_list, vnr_file_list_compare, NULL); +gboolean vnr_file_is_image_file(VnrFile* vnrfile) { + return vnrfile != NULL && !vnrfile->is_directory; } diff --git a/src/vnr-file.h b/src/vnr-file.h index f4cce26..8bd0e73 100644 --- a/src/vnr-file.h +++ b/src/vnr-file.h @@ -21,25 +21,35 @@ #define __VNR_FILE_H__ #include +#include +#include +#include +#include "vnr-callback-interface.h" G_BEGIN_DECLS -#define VNR_TYPE_FILE (vnr_file_get_type ()) +#define VNR_TYPE_FILE (vnr_file_get_type ()) #define VNR_FILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VNR_TYPE_FILE, VnrFile)) -#define VNR_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VNR_TYPE_FILE, VnrFileClass)) +#define VNR_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VNR_TYPE_FILE, VnrFileClass)) #define VNR_IS_FILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VNR_TYPE_FILE)) -#define VNR_IS_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VNR_TYPE_FILE)) -#define VNR_FILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), VNR_TYPE_FILE, VnrFileClass)) +#define VNR_IS_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VNR_TYPE_FILE)) +#define VNR_FILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), VNR_TYPE_FILE, VnrFileClass)) typedef struct _VnrFile VnrFile; typedef struct _VnrFileClass VnrFileClass; + struct _VnrFile { GObject parent; - const gchar *display_name; + gchar *display_name; const gchar *display_name_collate; - const gchar *path; + gchar *path; + + gboolean is_directory; + + GFileMonitor *monitor; + struct MonitoringData *monitoring_data; }; struct _VnrFileClass { @@ -51,9 +61,14 @@ GType vnr_file_get_type (void) G_GNUC_CONST; /* Constructors */ VnrFile *vnr_file_new (); -/* Actions */ -void vnr_file_load_uri_list (GSList *uri_list, GList **file_list, gboolean include_hidden, GError **error); -void vnr_file_load_single_uri (char *p_uri, GList **file_list, gboolean include_hidden, GError **error); + +VnrFile* +vnr_file_create_new(gchar *path, + char *display_name, + gboolean is_directory); +void vnr_file_destroy_data (VnrFile* vnrfile); +gboolean vnr_file_is_directory (VnrFile* vnrfile); +gboolean vnr_file_is_image_file(VnrFile* vnrfile); G_END_DECLS diff --git a/src/vnr-properties-dialog.c b/src/vnr-properties-dialog.c index ad055b5..52827dd 100644 --- a/src/vnr-properties-dialog.c +++ b/src/vnr-properties-dialog.c @@ -258,7 +258,7 @@ vnr_properties_dialog_update(VnrPropertiesDialog *dialog) gchar *filetype_desc = NULL; gchar *filesize_str = NULL; - get_file_info ((gchar*)VNR_FILE(dialog->vnr_win->file_list->data)->path, + get_file_info ((gchar*)VNR_FILE(dialog->vnr_win->tree->data)->path, &filesize, &filetype); if(filetype == NULL && filesize == 0) @@ -275,10 +275,10 @@ vnr_properties_dialog_update(VnrPropertiesDialog *dialog) filetype_desc = g_content_type_get_description (filetype); gtk_label_set_text(GTK_LABEL(dialog->name_label), - (gchar*)VNR_FILE(dialog->vnr_win->file_list->data)->display_name); + (gchar*)VNR_FILE(dialog->vnr_win->tree->data)->display_name); gtk_label_set_text(GTK_LABEL(dialog->location_label), - (gchar*)VNR_FILE(dialog->vnr_win->file_list->data)->path); + (gchar*)VNR_FILE(dialog->vnr_win->tree->data)->path); gtk_label_set_text(GTK_LABEL(dialog->type_label), filetype_desc); gtk_label_set_text(GTK_LABEL(dialog->size_label), filesize_str); @@ -307,7 +307,7 @@ vnr_properties_dialog_clear_metadata(VnrPropertiesDialog *dialog) g_list_free(children); } -static void +static void vnr_cb_add_metadata(const char *label, const char *value, void *user_data) { VnrPropertiesDialog *dialog = VNR_PROPERTIES_DIALOG(user_data); GtkWidget *temp_label; @@ -340,8 +340,8 @@ vnr_properties_dialog_update_metadata(VnrPropertiesDialog *dialog) vnr_properties_dialog_clear_metadata(dialog); uni_read_exiv2_map( - VNR_FILE(dialog->vnr_win->file_list->data)->path, - vnr_cb_add_metadata, + VNR_FILE(dialog->vnr_win->tree->data)->path, + vnr_cb_add_metadata, (void*)dialog); } diff --git a/src/vnr-tools.c b/src/vnr-tools.c index 76ab6ba..be44e13 100755 --- a/src/vnr-tools.c +++ b/src/vnr-tools.c @@ -145,39 +145,6 @@ vnr_tools_parse_uri_string_list_to_file_list (const gchar *uri_list) return g_slist_reverse (file_list); } -gint -compare_quarks (gconstpointer a, gconstpointer b) -{ - GQuark quark; - - quark = g_quark_from_string ((const gchar *) a); - - return quark - GPOINTER_TO_INT (b); -} - -void -get_position_of_element_in_list (GList *list, gint *current, gint *total) -{ - GList *it; - gint after, before; - - after = before = 0; - it = list; - - for(it = list; it != NULL; it = it->next) - { - after ++; - } - - for(it = list; it != NULL; it = it->prev) - { - before ++; - } - - *current = before; - *total = before + after - 1; -} - void vnr_tools_apply_embedded_orientation (GdkPixbufAnimation **anim) { diff --git a/src/vnr-tools.h b/src/vnr-tools.h index 5f8822e..1b749b1 100755 --- a/src/vnr-tools.h +++ b/src/vnr-tools.h @@ -26,7 +26,5 @@ void vnr_tools_fit_to_size_double (gdouble * w, gdouble * h, gint mw, gint mh GSList *vnr_tools_get_list_from_array (gchar **files); GSList *vnr_tools_parse_uri_string_list_to_file_list (const gchar *uri_list); void vnr_tools_apply_embedded_orientation (GdkPixbufAnimation **anim); -gint compare_quarks (gconstpointer a, gconstpointer b); -void get_position_of_element_in_list(GList *list, gint *current, gint *total); #endif /* __VNR_IMAGE_H__ */ diff --git a/src/vnr-tree.c b/src/vnr-tree.c new file mode 100644 index 0000000..5170036 --- /dev/null +++ b/src/vnr-tree.c @@ -0,0 +1,1019 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#include +#include + +#include + +#include "vnr-tree.h" + +#define UNUSED(x) (void)(x) + +typedef enum {RIGHT, LEFT} Direction; +typedef enum {CONTINUE, RETREAT} Course; + + +struct Preference_Settings { + gboolean include_hidden; + gboolean include_dirs; + gboolean set_file_monitor_for_file; + callback cb; + gpointer cb_data; +}; + + + +static GNode* +vnr_file_dir_content_to_list(VnrFile *vnrfile, + struct Preference_Settings* preference_settings, + GError **error); + +static gboolean +vnr_file_get_file_info(char *filepath, + VnrFile **vnrfile, + gboolean include_hidden, + GError **error); + +static void +vnr_file_set_file_monitor(GNode* tree, struct Preference_Settings* preference_settings); + + +static void +add_file_list_to_tree(GNode **tree, GList **file_list, struct Preference_Settings *preference_settings); + +static void +add_directory_list_to_tree(GNode **tree, GList **dir_list, struct Preference_Settings *preference_settings, GError **error); + +static gboolean +tree_contains_path(GNode *tree, char *path); + +GList * supported_mime_types; + + + +gint compare_quarks (gconstpointer a, gconstpointer b) { + GQuark quark = g_quark_from_string ((const gchar *) a); + return quark - GPOINTER_TO_INT (b); +} + + +static gint vnr_file_list_compare(gconstpointer a, gconstpointer b) { + return g_strcmp0(VNR_FILE(a)->display_name_collate, + VNR_FILE(b)->display_name_collate); +} + + + +/* Modified version of eog's eog_image_get_supported_mime_types */ +static GList * vnr_file_get_supported_mime_types(void) { + GSList *format_list, *it; + gchar **mime_types; + int i; + + if(!supported_mime_types) { + format_list = gdk_pixbuf_get_formats(); + + for(it = format_list; it != NULL; it = it->next) { + mime_types = gdk_pixbuf_format_get_mime_types((GdkPixbufFormat *) it->data); + + for(i = 0; mime_types[i] != NULL; i++) { + supported_mime_types = g_list_prepend(supported_mime_types, g_strdup(mime_types[i])); + } + + g_strfreev(mime_types); + } + + supported_mime_types = g_list_prepend(supported_mime_types, "image/vnd.microsoft.icon"); + supported_mime_types = g_list_sort(supported_mime_types, (GCompareFunc) compare_quarks); + + g_slist_free(format_list); + } + + return supported_mime_types; +} + +static gboolean vnr_file_is_supported_mime_type(const char *mime_type) { + GList *result; + + GQuark quark = g_quark_from_string(mime_type); + supported_mime_types = vnr_file_get_supported_mime_types(); + + result = g_list_find_custom(supported_mime_types, + GINT_TO_POINTER (quark), + (GCompareFunc) compare_quarks); + + return result != NULL; +} + + +static struct Preference_Settings* create_preference_settings(gboolean include_hidden, + gboolean include_dirs, + gboolean set_file_monitor_for_file, + callback cb, + gpointer cb_data) { + + struct Preference_Settings* preference_settings = malloc(sizeof(*preference_settings)); + preference_settings->include_hidden = include_hidden; + preference_settings->include_dirs = include_dirs; + preference_settings->set_file_monitor_for_file = set_file_monitor_for_file; + preference_settings->cb = cb; + preference_settings->cb_data = cb_data; + return preference_settings; +} + + +static void remove_file_from_tree(struct MonitoringData *monitoring_data, GFile *file) { + + GNode* tree = monitoring_data->tree; + callback tree_changed_callback = monitoring_data->cb; + gpointer cb_data = monitoring_data->cb_data; + + GNode *root = get_root_node(tree); + + char *file_path = g_file_get_path(file); + GNode* child = get_child_in_directory(tree, file_path); + + if(child != NULL) { + g_node_unlink(child); + free_current_tree(child); + } + + if(tree_changed_callback != NULL) { + tree_changed_callback(TRUE, file_path, child, root, cb_data); + } + + g_free(file_path); +} + +static void add_file_to_tree(struct MonitoringData *monitoring_data, GFile *file) { + + VnrFile* vnrfile_new = NULL; + + GNode* tree = monitoring_data->tree; + gboolean include_hidden = monitoring_data->include_hidden; + gboolean include_dirs = monitoring_data->include_dirs; + gboolean set_file_monitor_for_file = monitoring_data->set_file_monitor_for_file; + callback tree_changed_callback = monitoring_data->cb; + gpointer cb_data = monitoring_data->cb_data; + + GNode *root = get_root_node(tree); + char *file_path = g_file_get_path(file); + + if(!tree_contains_path(tree, file_path)) { + + vnr_file_get_file_info(file_path, + &vnrfile_new, + include_hidden, + NULL); + + gboolean file_added_to_tree = FALSE; + GNode *newnode = NULL; + if(vnr_file_is_directory(vnrfile_new)) { + if(include_dirs) { + // Newly created directory. It might already have been populated. + + struct Preference_Settings* preference_settings = create_preference_settings(include_hidden, + include_dirs, + set_file_monitor_for_file, + tree_changed_callback, + cb_data); + + newnode = vnr_file_dir_content_to_list(vnrfile_new, preference_settings, NULL); + add_node_in_tree(tree, newnode); + vnr_file_set_file_monitor(newnode, preference_settings); + + file_added_to_tree = TRUE; + free(preference_settings); + } + + } else if(vnr_file_is_image_file(vnrfile_new)) { + newnode = g_node_new(vnrfile_new); + add_node_in_tree(tree, newnode); + file_added_to_tree = TRUE; + } + + if(file_added_to_tree && tree_changed_callback != NULL) { + tree_changed_callback(FALSE, file_path, newnode, root, cb_data); + } + + if(!file_added_to_tree) { + vnr_file_destroy_data(vnrfile_new); + } + } + g_free(file_path); +} + + +static void +vnr_file_directory_updated(GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent type, + gpointer data) +{ + UNUSED(monitor); + UNUSED(other_file); + + VnrFile* vnrfile = data; + + switch (type) { + case G_FILE_MONITOR_EVENT_DELETED: + + remove_file_from_tree(vnrfile->monitoring_data, file); + break; + + case G_FILE_MONITOR_EVENT_CHANGED: // Fall-through + case G_FILE_MONITOR_EVENT_CREATED: + + add_file_to_tree(vnrfile->monitoring_data, file); + break; + + default: + break; + } +} + + + +static void +vnr_file_set_file_monitor(GNode* tree, struct Preference_Settings* preference_settings) +{ + VnrFile* vnrfile = tree->data; + GFile *file = g_file_new_for_path(vnrfile->path); + // It's not fatal if directory monitoring isn't supported, + // so set error to NULL. + vnrfile->monitor = g_file_monitor(file, + G_FILE_MONITOR_NONE, + NULL, + NULL); + g_object_unref(file); + + if(vnrfile->monitor) { + + // This will be freed when the VnrFile is destroyed. + struct MonitoringData* monitoring_data = malloc(sizeof(*monitoring_data)); + + monitoring_data->tree = tree; + monitoring_data->include_hidden = preference_settings->include_hidden; + monitoring_data->include_dirs = preference_settings->include_dirs; + monitoring_data->set_file_monitor_for_file = preference_settings->set_file_monitor_for_file; + monitoring_data->cb = preference_settings->cb; + monitoring_data->cb_data = preference_settings->cb_data; + + vnrfile->monitoring_data = monitoring_data; + + g_signal_connect(vnrfile->monitor, + "changed", + G_CALLBACK(vnr_file_directory_updated), + vnrfile); + } +} + + + +static gboolean +vnr_file_get_file_info(char *filepath, + VnrFile **vnrfile, + gboolean include_hidden, + GError **error) +{ + if(filepath == NULL) { + return FALSE; + } + GFile *file; + GFileInfo *fileinfo; + const char *mimetype; + char *display_name; + char *full_filepath; + gboolean file_info_success; + gboolean is_directory; + gboolean supported_mime_type = FALSE; + + *vnrfile = NULL; + file = g_file_new_for_path(filepath); + fileinfo = g_file_query_info(file, + G_FILE_ATTRIBUTE_STANDARD_TYPE"," + G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME"," + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE"," + G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN, + (GFileQueryInfoFlags) 0, NULL, error); + file_info_success = fileinfo != NULL; + + if(file_info_success && (include_hidden || !g_file_info_get_is_hidden(fileinfo))) { + is_directory = g_file_info_get_file_type(fileinfo) == G_FILE_TYPE_DIRECTORY; + display_name = g_strdup(g_file_info_get_display_name(fileinfo)); + + if(!is_directory) { + mimetype = g_file_info_get_content_type(fileinfo); + supported_mime_type = vnr_file_is_supported_mime_type(mimetype); + } + + if(is_directory || supported_mime_type) { + full_filepath = g_file_get_path(file); + *vnrfile = vnr_file_create_new(full_filepath, display_name, is_directory); + free(full_filepath); + } + free(display_name); + } + if(file_info_success) { + g_object_unref(fileinfo); + } + g_object_unref(file); + return file_info_success; +} + + +static void +vnr_file_add_file_to_lists_if_possible(gchar *filepath, + GList **dir_list, + GList **file_list, + struct Preference_Settings* preference_settings, + GError **error) +{ + VnrFile *vnrfile; + gboolean file_info_ok = vnr_file_get_file_info(filepath, + &vnrfile, + preference_settings->include_hidden, + error); + + if(file_info_ok && vnr_file_is_directory(vnrfile) && preference_settings->include_dirs) { + *dir_list = g_list_prepend( *dir_list, vnrfile); + } else if(file_info_ok && vnr_file_is_image_file(vnrfile)) { + *file_list = g_list_prepend(*file_list, vnrfile); + } else if(vnrfile != NULL) { + vnr_file_destroy_data(vnrfile); + } +} + +static void +vnr_append_file_and_dir_lists_to_tree(GNode **tree, + GList **dir_list, + GList **file_list, + struct Preference_Settings* preference_settings, + GError **error) +{ + add_file_list_to_tree(tree, file_list, preference_settings); + add_directory_list_to_tree(tree, dir_list, preference_settings, error); +} + +static void +add_file_list_to_tree(GNode **tree, + GList **file_list, + struct Preference_Settings *preference_settings) { + + *file_list = g_list_sort(*file_list, vnr_file_list_compare); + + while(*file_list != NULL) { + GNode *node = g_node_new((*file_list)->data); + add_node_in_tree(*tree, node); + + if(preference_settings->set_file_monitor_for_file) { + vnr_file_set_file_monitor(node, preference_settings); + } + + *file_list = g_list_next(*file_list); + } +} + +static void +add_directory_list_to_tree(GNode **tree, + GList **dir_list, + struct Preference_Settings *preference_settings, + GError **error) { + + *dir_list = g_list_sort(*dir_list, vnr_file_list_compare); + + struct Preference_Settings* dir_preference_settings = create_preference_settings(preference_settings->include_hidden, + preference_settings->include_dirs, + FALSE, + preference_settings->cb, + preference_settings->cb_data); + while(*dir_list != NULL) { + + GNode *node = vnr_file_dir_content_to_list((*dir_list)->data, + dir_preference_settings, + error); + vnr_file_set_file_monitor(node, preference_settings); + + add_node_in_tree(*tree, node); + *dir_list = g_list_next(*dir_list); + } + + free(dir_preference_settings); +} + + +static GNode* +vnr_file_dir_content_to_list(VnrFile *vnrfile, + struct Preference_Settings* preference_settings, + GError **error) +{ + GNode *tree = g_node_new(vnrfile); + GList *dir_list = NULL; + GList *file_list = NULL; + + GFile *file; + GFileEnumerator *f_enum; + GFileInfo *file_info; + + char* folder_path = vnrfile->path; + + file = g_file_new_for_path(folder_path); + f_enum = g_file_enumerate_children(file, + G_FILE_ATTRIBUTE_STANDARD_NAME"," + G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME"," + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE"," + G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + file_info = g_file_enumerator_next_file(f_enum, NULL, NULL); + + + while(file_info != NULL) { + char* child_path = g_strjoin(G_DIR_SEPARATOR_S, folder_path, + (char*)g_file_info_get_name (file_info), NULL); + + vnr_file_add_file_to_lists_if_possible(child_path, + &dir_list, + &file_list, + preference_settings, + error); + + free(child_path); + g_object_unref(file_info); + file_info = g_file_enumerator_next_file(f_enum, NULL, NULL); + } + + g_object_unref(file); + g_file_enumerator_close(f_enum, NULL, NULL); + g_object_unref(f_enum); + + vnr_append_file_and_dir_lists_to_tree(&tree, + &dir_list, + &file_list, + preference_settings, + error); + g_list_free(dir_list); + g_list_free(file_list); + return tree; +} + +static char* +vnr_get_parent_file_path(char *path) +{ + char* parent_path = NULL; + + GFile *file = g_file_new_for_path(path); + GFile *parent = g_file_get_parent(file); + + parent_path = g_file_get_path(parent); + + g_object_unref(parent); + g_object_unref(file); + + return parent_path; +} + + +/** + * Given the path @uri@, a tree will be created and returned. The path + * @uri@ may point to a file or a directory. If it is a file, the + * content of the whole directory that the file is located in will be + * used to populate the tree. + * + * The root node of the returned tree will always be the directory that + * @uri@ points to (if it is a file, then the directory where that file + * is placed). If @uri@ is a directory, then the first file in that + * directory will be returned. However, if @uri@ is a file, then the + * corresponding node down the child branches of the root node will be + * the node that is returned from this function. For example, if @uri@ + * is a file called "/tmp/bepa.png", and in the same directory there are + * also "/tmp/apa.jpg" and "/tmp/cepa.gif", then the structure created + * would be + * tmp + * ├─ apa.jpg + * ├─ bepa.png + * └─ cepa.gif + * Thus, the root would be "/tmp", with three children. However, the + * node corresponding to "/tmp/bepa.png" would be returned. If there are + * no files, the root node is returned. + * + * File monitors will be set on the directory that @uri@ points to (if + * it is a file, then the directory of that file will have a file + * monitor). If a file is removed from the file system, the file monitor + * will automatically remove the corresponding entry from the returned + * tree structure. If files are added to the file system, the + * corresponding entries will automatically be added to the returned + * tree structure. If @include_dirs@ is TRUE, all nested subdirectories + * will also have file monitors. When a file monitor is triggered (a + * file or directory is created or deleted), a call to the callback + * function @cb@ will be made. To it, @cb_data@ will be sent. + * In the example above, there is a file monitor on "/tmp", so if files + * are added to /tmp in the file system, they are also automatically + * added to the tree structure. + * + * Setting @include_hidden@ to TRUE will include hidden files and + * directories. + * Setting @include_dirs@ to TRUE will recursively go down all + * subdirectories, include them as well and set file monitors on them. + * @error@ will contain any errors that occurred in the process. + */ +GNode* create_tree_from_single_uri(char *uri, + gboolean include_hidden, + gboolean include_dirs, + callback cb, + gpointer cb_data, + GError **error) +{ + GNode *tree = NULL; + VnrFile* vnrfile; + gboolean file_info_ok; + + struct Preference_Settings* preference_settings = create_preference_settings(include_hidden, + include_dirs, + FALSE, + cb, + cb_data); + + file_info_ok = vnr_file_get_file_info(uri, + &vnrfile, + include_hidden, + error); + + if(file_info_ok && vnrfile != NULL && vnrfile->is_directory) { + tree = vnr_file_dir_content_to_list(vnrfile, + preference_settings, + error); + vnr_file_set_file_monitor(tree, preference_settings); + + tree = get_next_in_tree(tree); + + } else if(file_info_ok && vnrfile != NULL) { + vnr_file_destroy_data(vnrfile); + char* parent_path = vnr_get_parent_file_path(uri); + + file_info_ok = vnr_file_get_file_info(parent_path, + &vnrfile, + include_hidden, + error); + + if(file_info_ok && vnrfile != NULL) { + tree = vnr_file_dir_content_to_list(vnrfile, + preference_settings, + error); + vnr_file_set_file_monitor(tree, preference_settings); + } + + GNode *node = get_child_in_directory(tree, uri); + if(node == NULL) { + tree = get_next_in_tree(tree); + } else { + tree = node; + } + free(parent_path); + } + + free(preference_settings); + return tree; +} + +/** + * Given a list of paths @uri_list@, a tree will be created and + * returned. The paths in @uri_list@ may point to files or directories. + * + * The root node of the returned tree will not contain a file or + * directory itself, but instead act as a parent to the children beneath + * it, i.e. the content of @uri_list@. The files and directories in + * @uri_list@ do not have to be in the same directory. For example, if + * @uri_list@ consists of "/tmp/subdir", "/tmp/bepa.png", and + * "/tmp/somedir/apa.jpg", then the structure created will be + * + * ├─ apa.jpg + * ├─ bepa.png + * └─ /subdir + * Thus, the root would contain the three children in @uri_list@, but + * the root would not contain a file or directory itself. Despite the + * files "apa.jpg" and "bepa.png" not being in the same directory, they + * are placed as siblings in the tree. + * + * The first node containing a file in the tree structure will be + * returned. Note that this is not neccessarily the first node in + * @uri_list@, since the tree structure is alphabetically sorted by name + * (and directories are placed after files). In the case above, the + * returned node would be the one containing "apa.jpg". If there are no + * files, then the root node is returned. + * + * File monitors will be set on all the files and directories in + * @uri_list@. If a file is removed from the file system, the file + * monitor will automatically remove the corresponding entry from the + * returned tree structure. If files are added to the file system, the + * corresponding entries will automatically be added to the returned + * tree structure. If @include_dirs@ is TRUE, all nested subdirectories + * will also have file monitors. When a file monitor is triggered (a + * file or directory is created or deleted), a call to the callback + * function @cb@ will be made. To it, @cb_data@ will be sent. + * In the example above, the files "apa.jpg", "bepa.png" and "/subdir" + * will have file monitors. Thus, removing "bepa.png" from the file + * system will remove it from the tree structure as well. Adding a file + * "/tmp/cepa.gif" will do nothing to the tree, since "/tmp" has no file + * monitor. However, Adding a file "/tmp/subdir/cepa.gif" will add it to + * the tree as well, since "/subdir" does have a file monitor. + * + * Setting @include_hidden@ to TRUE will include hidden files and + * directories. + * Setting @include_dirs@ to TRUE will recursively go down all + * subdirectories, include them as well and set file monitors on them. + * @error@ will contain any errors that occurred in the process. + */ +GNode* create_tree_from_uri_list(GSList *uri_list, + gboolean include_hidden, + gboolean include_dirs, + callback cb, + gpointer cb_data, + GError **error) +{ + GNode *tree = g_node_new(NULL); + GList *dir_list = NULL; + GList *file_list = NULL; + + + struct Preference_Settings* dir_preference_settings = create_preference_settings(include_hidden, + TRUE, + TRUE, + cb, + cb_data); + + while(uri_list != NULL) { + + vnr_file_add_file_to_lists_if_possible(uri_list->data, + &dir_list, + &file_list, + dir_preference_settings, + error); + g_clear_error(error); + uri_list = g_slist_next(uri_list); + } + + struct Preference_Settings* preference_settings = create_preference_settings(include_hidden, + include_dirs, + TRUE, + cb, + cb_data); + vnr_append_file_and_dir_lists_to_tree(&tree, + &dir_list, + &file_list, + preference_settings, + error); + + tree = get_next_in_tree(tree); + + g_list_free(dir_list); + g_list_free(file_list); + free(dir_preference_settings); + free(preference_settings); + return tree; +} + + + + + + +static gboolean is_leaf(GNode *node) { + VnrFile* vnrfile = node->data; + return vnrfile != NULL && !vnrfile->is_directory; // A leaf in the tree + // can represent an empty directory. Otherwise we could do G_NODE_IS_LEAF(node) +} + +static gboolean has_more_siblings_in_direction(GNode *tree, Direction direction) { + return tree != (direction == RIGHT ? g_node_last_sibling(tree) : g_node_first_sibling(tree)); +} + +static GNode* get_prev_or_next(GNode* tree, Direction direction) { + return direction == RIGHT ? g_node_next_sibling(tree) : g_node_prev_sibling(tree); +} +static GNode* get_first_or_last(GNode* tree, Direction direction) { + return direction == RIGHT ? g_node_first_child(tree) : g_node_last_child(tree); +} + +static GNode* recursively_find_prev_or_next(GNode *tree, GNode *original_node, Direction direction, Course course) { + + if(tree == NULL || tree == original_node) { + return original_node; + } + if(is_leaf(tree)) { + return tree; + } + + // It is a directory. + + GNode *node; + Course new_course = CONTINUE; + if(G_NODE_IS_ROOT(tree)) { + node = get_first_or_last(tree, direction); + + } else if(has_children(tree) && course != RETREAT) { + node = get_first_or_last(tree, direction); + + } else if(has_more_siblings_in_direction(tree, direction)) { + node = get_prev_or_next(tree, direction); + + } else { + node = tree->parent; + new_course = RETREAT; + } + return recursively_find_prev_or_next(node, original_node, direction, new_course); +} + +static GNode* get_prev_or_next_in_tree(GNode *tree, Direction direction) { + + if(tree == NULL) { + return NULL; + } + GNode *next = tree; + Course course = CONTINUE; + + if(G_NODE_IS_ROOT(tree)) { + // Is root + next = get_first_or_last(tree, direction); + + } else if(is_leaf(tree) && has_more_siblings_in_direction(tree, direction)) { + // Is leaf with more siblings + next = get_prev_or_next(tree, direction); + + } else if(is_leaf(tree) && !has_more_siblings_in_direction(tree, direction)) { + // Is leaf with no more siblings + next = tree->parent; + course = RETREAT; + + } else if(!is_leaf(tree) && has_children(tree)) { + // Is directory with children + next = get_first_or_last(tree, direction); + + } else if(!is_leaf(tree) && has_more_siblings_in_direction(tree, direction)) { + // Is directory without children but with more siblings + next = get_prev_or_next(tree, direction); + + } else if(!is_leaf(tree) && !has_children(tree) && !has_more_siblings_in_direction(tree, direction)) { + // Is directory without children and with no more siblings + next = tree->parent; + course = RETREAT; + } + + return recursively_find_prev_or_next(next, tree, direction, course); +} + +/** + * Returns the first file (i.e. not directory) in the given @tree@. + * Will climb up the structure relative to @tree@ if needed. + * If there is no such file, @tree@ will be returned. + */ +GNode* get_first_in_tree(GNode* tree) { + GNode *node = get_root_node(tree); + return get_prev_or_next_in_tree(node, RIGHT); +} + +/** + * Returns the last file (i.e. not directory) in the given @tree@. + * Will climb up the structure relative to @tree@ if needed. + * If there is no such file, @tree@ will be returned. + */ +GNode* get_last_in_tree(GNode* tree) { + GNode *node = get_first_in_tree(tree); + if(node == NULL || node->data == NULL) { + return tree; + } else { + return get_prev_or_next_in_tree(node, LEFT); + } +} + + + + +static gboolean found_position_where_node_should_be_inserted(GNode *currnode, GNode *newnode) { + if(currnode == NULL) { + return TRUE; + } + + gboolean currnode_is_dir = !is_leaf(currnode); + gboolean newnode_is_file = is_leaf(newnode); + gboolean both_nodes_are_of_same_type = is_leaf(currnode) == is_leaf(newnode); + return (currnode_is_dir && newnode_is_file) || + (both_nodes_are_of_same_type && g_strcmp0(((VnrFile*) currnode->data)->display_name_collate, ((VnrFile*) newnode->data)->display_name_collate) > 0); +} + +/** + * Adds @node@ as a child of @tree@, sorted by @display_name_collate@. + * @tree@ must be a directory, not a file; @node@ may be a file or a + * directory. When sorting, files will be inserted before directories. + * If @node@ is already present, @tree@ will remain unchanged. + */ +void add_node_in_tree(GNode *tree, GNode *node) { + if(node == NULL || node->data == NULL || tree == NULL || is_leaf(tree)) { + return; + } + GNode *child = get_first_or_last(tree, RIGHT); + + gboolean already_present = FALSE; + int i = 0; + while(!found_position_where_node_should_be_inserted(child, node)) { + i++; + if(g_strcmp0(((VnrFile*) child->data)->path, ((VnrFile*) node->data)->path) == 0) { + already_present = TRUE; + break; + } + if(!has_more_siblings(child)) { + break; + } + child = g_node_next_sibling(child); + } + if(!already_present) { + g_node_insert(tree, i, node); + } +} + + +static gboolean node_has_path(GNode *node, char *path) { + if(node == NULL || node->data == NULL) { + return FALSE; + } + VnrFile *vnrfile = node->data; + return g_strcmp0(vnrfile->path, path) == 0; +} + + +static GNode* recursively_get_child_in_directory(GNode *tree, char* path) { + if(node_has_path(tree, path)) { + return tree; + } + GNode *child, *dirchild; + + if(has_children(tree)) { + child = g_node_first_child(tree); + dirchild = recursively_get_child_in_directory(child, path); + + while(dirchild == NULL && has_more_siblings(child)) { + child = g_node_next_sibling(child); + dirchild = recursively_get_child_in_directory(child, path); + } + return dirchild; + } + return NULL; +} + + +/** + * Will move to the topmost root of @tree@, traverse the whole structure + * and return the node (file or directory) whose path is equal to + * @path@. If no such node exists in the structure, NULL is returned. + */ +GNode* get_child_in_directory(GNode *tree, char* path) { + return recursively_get_child_in_directory(get_root_node(tree), path); +} + +static gboolean tree_contains_path(GNode *tree, char *path) { + return get_child_in_directory(tree, path) != NULL; +} + +/** + * Returns whether or not the given @tree@ has children + * (files or directories). + */ +gboolean has_children(GNode *tree) { + return tree != NULL && g_node_n_children(tree) > 0; +} + +/** + * Returns whether or not the given @tree@ has any more siblings (files + * or directories). If a @tree@ has n children, then giving any of the + * first n-1 children of @tree@ as input to this function would return + * TRUE. For child n, this function would return FALSE as there are no + * more children after it. + */ +gboolean has_more_siblings(GNode *tree) { + return has_more_siblings_in_direction(tree, RIGHT); +} + + + +/** + * Returns the next file (i.e. not directory) in the given @tree@. + * Will climb up the structure relative to @tree@ or "wrap around" if + * needed. If there is no such file, @tree@ will be returned. + */ +GNode* get_next_in_tree(GNode *tree) { + return get_prev_or_next_in_tree(tree, RIGHT); +} + +/** + * Returns the previous file (i.e. not directory) in the given @tree@. + * Will climb up the structure relative to @tree@ or "wrap around" if + * needed. If there is no such file, @tree@ will be returned. + */ +GNode* get_prev_in_tree(GNode *tree) { + return get_prev_or_next_in_tree(tree, LEFT); +} + +static void get_number_of_leaves(GNode *tree, GNode *node_to_look_for, int *current, int *total) { + if(tree == NULL) { + return; + } + + if(is_leaf(tree)) { + *total = *total + 1; + } + if(tree == node_to_look_for) { + *current = *total; + } + + GNode *child = get_first_or_last(tree, RIGHT); + while(child != NULL) { + + get_number_of_leaves(child, node_to_look_for, current, total); + if(!has_more_siblings(child)) { + break; + } + child = get_prev_or_next(child, RIGHT); + } +} + +/** + * This function will move to the root from @tree@, and go through the + * whole structure. All files (i.e. not directories) will be counted, + * and that number will be placed in @total@. The position of @tree@ in + * the structure will be placed in @tree_position@. + */ +void get_leaf_position(GNode *tree, int *tree_position, int *total) { + + *tree_position = -1; + *total = 0; + + get_number_of_leaves(get_root_node(tree), tree, tree_position, total); +} + +/** + * This function will move to the root from @tree@, and go through the + * whole structure. All files (i.e. not directories) will be counted, + * and that number will be returned. + */ +int get_total_number_of_leaves(GNode *tree) { + + int tree_position = -1; + int total = 0; + + get_number_of_leaves(get_root_node(tree), NULL, &tree_position, &total); + return total; +} + +/** + * Returns the topmost root of @tree@. + */ +GNode* get_root_node(GNode *tree) { + GNode *node = tree; + while (node != NULL && node->parent != NULL) { + node = node->parent; + } + return node; +} + + +static gboolean destroy_node(GNode *node, gpointer data) { + UNUSED(data); + vnr_file_destroy_data(node->data); + return FALSE; +} + +/** + * Frees @tree@. If it is a sub-tree, the rest of the tree will be left + * alone. Traverses the whole of @tree@ and destroys the nodes as well. + */ +void free_current_tree(GNode *tree) { + g_node_traverse(tree, G_POST_ORDER, G_TRAVERSE_ALL, -1, destroy_node, NULL); + g_node_destroy(tree); +} + +/** + * Moves to the topmost root of @tree@ and frees the whole structure. + * Traverses the whole tree and destroys the nodes as well. + */ +void free_whole_tree(GNode *tree) { + GNode *node = get_root_node(tree); + free_current_tree(node); +} diff --git a/src/vnr-tree.h b/src/vnr-tree.h new file mode 100644 index 0000000..ba22f8c --- /dev/null +++ b/src/vnr-tree.h @@ -0,0 +1,232 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#ifndef tree_H +#define tree_H + +#include +#include "vnr-callback-interface.h" +#include "vnr-file.h" + + +/** + * Given the path @uri@, a tree will be created and returned. The path + * @uri@ may point to a file or a directory. If it is a file, the + * content of the whole directory that the file is located in will be + * used to populate the tree. + * + * The root node of the returned tree will always be the directory that + * @uri@ points to (if it is a file, then the directory where that file + * is placed). If @uri@ is a directory, then the first file in that + * directory will be returned. However, if @uri@ is a file, then the + * corresponding node down the child branches of the root node will be + * the node that is returned from this function. For example, if @uri@ + * is a file called "/tmp/bepa.png", and in the same directory there are + * also "/tmp/apa.jpg" and "/tmp/cepa.gif", then the structure created + * would be + * tmp + * ├─ apa.jpg + * ├─ bepa.png + * └─ cepa.gif + * Thus, the root would be "/tmp", with three children. However, the + * node corresponding to "/tmp/bepa.png" would be returned. If there are + * no files, the root node is returned. + * + * File monitors will be set on the directory that @uri@ points to (if + * it is a file, then the directory of that file will have a file + * monitor). If a file is removed from the file system, the file monitor + * will automatically remove the corresponding entry from the returned + * tree structure. If files are added to the file system, the + * corresponding entries will automatically be added to the returned + * tree structure. If @include_dirs@ is TRUE, all nested subdirectories + * will also have file monitors. When a file monitor is triggered (a + * file or directory is created or deleted), a call to the callback + * function @cb@ will be made. To it, @cb_data@ will be sent. + * In the example above, there is a file monitor on "/tmp", so if files + * are added to /tmp in the file system, they are also automatically + * added to the tree structure. + * + * Setting @include_hidden@ to TRUE will include hidden files and + * directories. + * Setting @include_dirs@ to TRUE will recursively go down all + * subdirectories, include them as well and set file monitors on them. + * @error@ will contain any errors that occurred in the process. + */ +GNode* create_tree_from_single_uri(char *uri, + gboolean include_hidden, + gboolean recursive, + callback cb, + gpointer cb_data, + GError **error); + + +/** + * Given a list of paths @uri_list@, a tree will be created and + * returned. The paths in @uri_list@ may point to files or directories. + * + * The root node of the returned tree will not contain a file or + * directory itself, but instead act as a parent to the children beneath + * it, i.e. the content of @uri_list@. The files and directories in + * @uri_list@ do not have to be in the same directory. For example, if + * @uri_list@ consists of "/tmp/subdir", "/tmp/bepa.png", and + * "/tmp/somedir/apa.jpg", then the structure created will be + * + * ├─ apa.jpg + * ├─ bepa.png + * └─ /subdir + * Thus, the root would contain the three children in @uri_list@, but + * the root would not contain a file or directory itself. Despite the + * files "apa.jpg" and "bepa.png" not being in the same directory, they + * are placed as siblings in the tree. + * + * The first node containing a file in the tree structure will be + * returned. Note that this is not neccessarily the first node in + * @uri_list@, since the tree structure is alphabetically sorted by name + * (and directories are placed after files). In the case above, the + * returned node would be the one containing "apa.jpg". If there are no + * files, then the root node is returned. + * + * File monitors will be set on all the files and directories in + * @uri_list@. If a file is removed from the file system, the file + * monitor will automatically remove the corresponding entry from the + * returned tree structure. If files are added to the file system, the + * corresponding entries will automatically be added to the returned + * tree structure. If @include_dirs@ is TRUE, all nested subdirectories + * will also have file monitors. When a file monitor is triggered (a + * file or directory is created or deleted), a call to the callback + * function @cb@ will be made. To it, @cb_data@ will be sent. + * In the example above, the files "apa.jpg", "bepa.png" and "/subdir" + * will have file monitors. Thus, removing "bepa.png" from the file + * system will remove it from the tree structure as well. Adding a file + * "/tmp/cepa.gif" will do nothing to the tree, since "/tmp" has no file + * monitor. However, Adding a file "/tmp/subdir/cepa.gif" will add it to + * the tree as well, since "/subdir" does have a file monitor. + * + * Setting @include_hidden@ to TRUE will include hidden files and + * directories. + * Setting @include_dirs@ to TRUE will recursively go down all + * subdirectories, include them as well and set file monitors on them. + * @error@ will contain any errors that occurred in the process. + */ +GNode* create_tree_from_uri_list(GSList *uri_list, + gboolean include_hidden, + gboolean recursive, + callback cb, + gpointer cb_data, + GError **error); + + +/** + * Adds @node@ as a child of @tree@, sorted by @display_name_collate@. + * @tree@ must be a directory, not a file; @node@ may be a file or a + * directory. When sorting, files will be inserted before directories. + * If @node@ is already present, @tree@ will remain unchanged. + */ +void add_node_in_tree(GNode *tree, GNode *node); + + +/** + * Returns the next file (i.e. not directory) in the given @tree@. + * Will climb up the structure relative to @tree@ or "wrap around" if + * needed. If there is no such file, @tree@ will be returned. + */ +GNode* get_next_in_tree(GNode *tree); + +/** + * Returns the previous file (i.e. not directory) in the given @tree@. + * Will climb up the structure relative to @tree@ or "wrap around" if + * needed. If there is no such file, @tree@ will be returned. + */ +GNode* get_prev_in_tree(GNode *tree); + +/** + * Returns the first file (i.e. not directory) in the given @tree@. + * Will climb up the structure relative to @tree@ if needed. + * If there is no such file, @tree@ will be returned. + */ +GNode* get_first_in_tree(GNode* tree); + +/** + * Returns the last file (i.e. not directory) in the given @tree@. + * Will climb up the structure relative to @tree@ if needed. + * If there is no such file, @tree@ will be returned. + */ +GNode* get_last_in_tree(GNode* tree); + +/** + * Will move to the topmost root of @tree@, traverse the whole structure + * and return the node (file or directory) whose path is equal to + * @path@. If no such node exists in the structure, NULL is returned. + */ +GNode* get_child_in_directory(GNode *tree, char* path); + + + +/** + * This function will move to the root from @tree@, and go through the + * whole structure. All files (i.e. not directories) will be counted, + * and that number will be placed in @total@. The position of @tree@ in + * the structure will be placed in @tree_position@. + */ +void get_leaf_position(GNode *tree, int *tree_position, int *total); + +/** + * This function will move to the root from @tree@, and go through the + * whole structure. All files (i.e. not directories) will be counted, + * and that number will be returned. + */ +int get_total_number_of_leaves(GNode *tree); + + + +/** + * Returns whether or not the given @tree@ has children + * (files or directories). + */ +gboolean has_children(GNode *tree); + +/** + * Returns whether or not the given @tree@ has any more siblings (files + * or directories). If a @tree@ has n children, then giving any of the + * first n-1 children of @tree@ as input to this function would return + * TRUE. For child n, this function would return FALSE as there are no + * more children after it. + */ +gboolean has_more_siblings(GNode *tree); + + +/** + * Returns the topmost root of @tree@. + */ +GNode* get_root_node(GNode *tree); + + +/** + * Frees @tree@. If it is a sub-tree, the rest of the tree will be left + * alone. Traverses the whole of @tree@ and destroys the nodes as well. + */ +void free_current_tree(GNode *tree); + +/** + * Moves to the topmost root of @tree@ and frees the whole structure. + * Traverses the whole tree and destroys the nodes as well. + */ +void free_whole_tree(GNode *tree); + +#endif // tree_H diff --git a/src/vnr-window.c b/src/vnr-window.c index 435b45d..aec2f0d 100755 --- a/src/vnr-window.c +++ b/src/vnr-window.c @@ -31,6 +31,7 @@ #include #include #include "vnr-window.h" +#include "vnr-tree.h" #include "uni-scroll-win.h" #include "uni-anim-view.h" #include "vnr-tools.h" @@ -246,7 +247,7 @@ vnr_window_update_openwith_menu (VnrWindow *window) GList *apps; guint action_id = 0; - file = g_file_new_for_path ((gchar*)VNR_FILE(window->file_list->data)->path); + file = g_file_new_for_path ((gchar*)VNR_FILE(window->tree->data)->path); file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, 0, NULL, NULL); @@ -389,9 +390,9 @@ update_fs_filename_label(VnrWindow *window) gint position, total; char *buf; - get_position_of_element_in_list(window->file_list, &position, &total); + get_leaf_position(window->tree, &position, &total); buf = g_strdup_printf ("%s - %i/%i", - VNR_FILE(window->file_list->data)->display_name, + VNR_FILE(window->tree->data)->display_name, position, total); gtk_label_set_text(GTK_LABEL(window->fs_filename_label), buf); @@ -402,7 +403,7 @@ update_fs_filename_label(VnrWindow *window) static gboolean next_image_src(VnrWindow *window) { - if(g_list_length(g_list_first(window->file_list)) <= 1) + if(get_total_number_of_leaves(window->tree) <= 1) return FALSE; else vnr_window_next(window, FALSE); @@ -478,7 +479,6 @@ get_fs_controls(VnrWindow *window) window->toggle_btn = widget; /* Create spin button to adjust slideshow's timeout */ - //spinner_adj = (GtkAdjustment *) gtk_adjustment_new (5, 1.0, 30.0, 1.0, 1.0, 0); spinner_adj = (GtkAdjustment *) gtk_adjustment_new (window->prefs->slideshow_timeout, 1.0, 30.0, 1.0, 1.0, 0); widget = gtk_spin_button_new (spinner_adj, 1.0, 0); gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON(widget), TRUE); @@ -679,26 +679,23 @@ restart_slideshow(VnrWindow *window) window); } -static void -allow_slideshow(VnrWindow *window) +void +allow_or_deny_slideshow(VnrWindow *window, gboolean allow) { - if(window->slideshow) - return; - - window->slideshow = TRUE; + if(window->slideshow != allow) { + window->slideshow = allow; + gtk_widget_set_sensitive(window->toggle_btn, allow); + } +} - gtk_widget_set_sensitive(window->toggle_btn, TRUE); +static void +allow_slideshow(VnrWindow *window) { + allow_or_deny_slideshow(window, TRUE); } void -deny_slideshow(VnrWindow *window) -{ - if(!window->slideshow) - return; - - window->slideshow = FALSE; - - gtk_widget_set_sensitive(window->toggle_btn, FALSE); +deny_slideshow(VnrWindow *window) { + allow_or_deny_slideshow(window, FALSE); } static void @@ -836,7 +833,7 @@ open_with_launch_application_cb (GtkAction *action, VnrWindow *window) GFile *file; GList *files = NULL; - file = g_file_new_for_path ((gchar*)VNR_FILE(window->file_list->data)->path); + file = g_file_new_for_path ((gchar*)VNR_FILE(window->tree->data)->path); app = g_object_get_data (G_OBJECT (action), "app"); files = g_list_append (files, file); @@ -922,11 +919,13 @@ save_image_cb (GtkWidget *widget, VnrWindow *window) /* This makes the cursor show NOW */ gdk_flush(); - if(window->prefs->behavior_modify == VNR_PREFS_MODIFY_ASK) + if(window->prefs->behavior_modify == VNR_PREFS_MODIFY_ASK) { vnr_message_area_hide(VNR_MESSAGE_AREA(window->msg_area)); + } + char* path = VNR_FILE(window->tree->data)->path; /* Store exiv2 metadata to cache, so we can restore it afterwards */ - uni_read_exiv2_to_cache(VNR_FILE(window->file_list->data)->path); + uni_read_exiv2_to_cache(path); if(g_strcmp0(window->writable_format_name, "jpeg" ) == 0) { @@ -934,7 +933,7 @@ save_image_cb (GtkWidget *widget, VnrWindow *window) quality = g_strdup_printf ("%i", window->prefs->jpeg_quality); gdk_pixbuf_save (uni_image_view_get_pixbuf(UNI_IMAGE_VIEW(window->view)), - VNR_FILE(window->file_list->data)->path, "jpeg", + path, "jpeg", &error, "quality", quality, NULL); g_free(quality); } @@ -944,17 +943,17 @@ save_image_cb (GtkWidget *widget, VnrWindow *window) compression = g_strdup_printf ("%i", window->prefs->png_compression); gdk_pixbuf_save (uni_image_view_get_pixbuf(UNI_IMAGE_VIEW(window->view)), - VNR_FILE(window->file_list->data)->path, "png", + path, "png", &error, "compression", compression, NULL); g_free(compression); } else { gdk_pixbuf_save (uni_image_view_get_pixbuf(UNI_IMAGE_VIEW(window->view)), - VNR_FILE(window->file_list->data)->path, + path, window->writable_format_name, &error, NULL); } - uni_write_exiv2_from_cache(VNR_FILE(window->file_list->data)->path); + uni_write_exiv2_from_cache(path); if(!window->cursor_is_hidden) gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)), gdk_cursor_new(GDK_LEFT_PTR)); @@ -987,25 +986,25 @@ static void vnr_window_main_menu_position (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data) { VnrWindow *window = VNR_WINDOW(user_data); - GtkWidget *button = window->properties_button; + GtkWidget *button = window->properties_button; GdkWindow *gdk_window = gtk_widget_get_window(button); - GtkRequisition req = {0, 0}; - GtkAllocation toolbar_allocation; - GtkAllocation button_allocation; + GtkRequisition req = {0, 0}; + GtkAllocation toolbar_allocation; + GtkAllocation button_allocation; gdk_window_get_position(gdk_window, x, y); - // in maximuzed and fullscreen states gdk_window_get_position returns 0 - if ( *x == 0 && gtk_widget_get_visible ( get_fs_controls(window)) ) { - GtkAllocation allocation; + // In maximized and fullscreen states gdk_window_get_position returns 0 + if (*x == 0 && gtk_widget_get_visible(get_fs_controls(window))) { + GtkAllocation allocation; - gtk_widget_get_allocation(get_fs_controls(window), &allocation); + gtk_widget_get_allocation(get_fs_controls(window), &allocation); *x -= allocation.width; } - gtk_widget_size_request(GTK_WIDGET(menu), &req); - gtk_widget_get_allocation(window->toolbar, &toolbar_allocation); - gtk_widget_get_allocation(button, &button_allocation); + gtk_widget_size_request(GTK_WIDGET(menu), &req); + gtk_widget_get_allocation(window->toolbar, &toolbar_allocation); + gtk_widget_get_allocation(button, &button_allocation); *x += toolbar_allocation.width - req.width; *y += button_allocation.height; @@ -1014,11 +1013,15 @@ vnr_window_main_menu_position (GtkMenu *menu, gint *x, gint *y, gboolean *push_i static void vnr_window_cmd_open_menu (GtkToggleAction *action, VnrWindow *window) { - if( !gtk_toggle_action_get_active (action)) { - return; + if(gtk_toggle_action_get_active (action)) { + gtk_menu_popup(GTK_MENU(window->button_menu), + NULL, + NULL, + vnr_window_main_menu_position, + window, + 0, + gtk_get_current_event_time()); } - gtk_menu_popup(GTK_MENU(window->button_menu), NULL, NULL, vnr_window_main_menu_position, window, 0, gtk_get_current_event_time()); - return; } static void @@ -1042,28 +1045,27 @@ window_realize_cb(GtkWidget *widget, gpointer user_data) { if ( VNR_WINDOW(widget)->prefs->start_maximized ) { vnr_window_open(VNR_WINDOW(widget), FALSE); - } - else + } + else { GdkScreen *screen; GdkRectangle monitor; screen = gtk_window_get_screen (GTK_WINDOW (widget)); gdk_screen_get_monitor_geometry (screen, - gdk_screen_get_monitor_at_window (screen, - gtk_widget_get_window (widget)), + gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (widget)), &monitor); - VNR_WINDOW(widget)->max_width = monitor.width * 0.9 - 100; + VNR_WINDOW(widget)->max_width = monitor.width * 0.9 - 100; VNR_WINDOW(widget)->max_height = monitor.height * 0.9 - 100; vnr_window_open(VNR_WINDOW(widget), TRUE); } - if ( VNR_WINDOW(widget)->prefs->start_slideshow && VNR_WINDOW(widget)->file_list != NULL ) { + if ( VNR_WINDOW(widget)->prefs->start_slideshow && VNR_WINDOW(widget)->tree != NULL ) { vnr_window_fullscreen(VNR_WINDOW(widget)); VNR_WINDOW(widget)->mode = VNR_WINDOW_MODE_NORMAL; allow_slideshow(VNR_WINDOW(widget)); start_slideshow(VNR_WINDOW(widget)); - } else if ( VNR_WINDOW(widget)->prefs->start_fullscreen && VNR_WINDOW(widget)->file_list != NULL ) { + } else if ( VNR_WINDOW(widget)->prefs->start_fullscreen && VNR_WINDOW(widget)->tree != NULL ) { vnr_window_fullscreen(VNR_WINDOW(widget)); } } @@ -1094,24 +1096,29 @@ window_destroy_cb (GtkObject *object, gpointer user_data) } static void -zoom_changed_cb (UniImageView *view, VnrWindow *window) +set_window_title(UniImageView *view, VnrWindow *window) { - gint position, total; char *buf = NULL; + gint position, total; + get_leaf_position(window->tree, &position, &total); + + buf = g_strdup_printf ("%s%s - %i/%i - %ix%i - %i%%", (window->modifications)?"*":"", + VNR_FILE(window->tree->data)->display_name, + position, total, + window->current_image_width, window->current_image_height, + (int)(view->zoom*100.)); + + gtk_window_set_title (GTK_WINDOW(window), buf); + g_free(buf); +} +static void +zoom_changed_cb (UniImageView *view, VnrWindow *window) +{ /* Change the info, only if there is an image * (vnr_window_close isn't called on the current image) */ - if(gtk_action_group_get_sensitive (window->actions_image)) - { - get_position_of_element_in_list(window->file_list, &position, &total); - buf = g_strdup_printf ("%s%s - %i/%i - %ix%i - %i%%", (window->modifications)?"*":"", - VNR_FILE(window->file_list->data)->display_name, - position, total, - window->current_image_width, window->current_image_height, - (int)(view->zoom*100.)); - - gtk_window_set_title (GTK_WINDOW(window), buf); - g_free(buf); + if(gtk_action_group_get_sensitive (window->actions_image)) { + set_window_title(view, window); } } @@ -1126,7 +1133,7 @@ window_drag_begin_cb (GtkWidget *widget, { gchar *uris[2]; - uris[0] = g_filename_to_uri((gchar*)VNR_FILE(VNR_WINDOW(user_data)->file_list->data)->path, NULL, NULL); + uris[0] = g_filename_to_uri((gchar*)VNR_FILE(VNR_WINDOW(user_data)->tree->data)->path, NULL, NULL); uris[1] = NULL; gtk_selection_data_set_uris (data, uris); @@ -1141,9 +1148,16 @@ file_open_dialog_response_cb (GtkWidget *dialog, { if (response_id == GTK_RESPONSE_ACCEPT) { + gboolean open_recursively = FALSE; GSList *uri_list = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (dialog)); g_return_if_fail(uri_list != NULL); - vnr_window_open_from_list(window, uri_list); + + GtkWidget *toggle_btn = gtk_file_chooser_get_extra_widget(GTK_FILE_CHOOSER(dialog)); + if(toggle_btn != NULL) { + open_recursively = gtk_toggle_button_get_active((GtkToggleButton *) toggle_btn); + } + + vnr_window_open_from_list(window, uri_list, open_recursively); g_slist_free_full(uri_list, g_free); } @@ -1159,19 +1173,15 @@ vnr_window_cmd_preferences(GtkAction *action, gpointer user_data) static void vnr_window_cmd_flip_horizontal(GtkAction *action, VnrWindow *window) { - if ( !gtk_action_group_get_sensitive(window->actions_static_image) ) - return; - - flip_pixbuf(window, TRUE); + if(gtk_action_group_get_sensitive(window->actions_static_image)) + flip_pixbuf(window, TRUE); } static void vnr_window_cmd_flip_vertical(GtkAction *action, VnrWindow *window) { - if ( !gtk_action_group_get_sensitive(window->actions_static_image) ) - return; - - flip_pixbuf(window, FALSE); + if(gtk_action_group_get_sensitive(window->actions_static_image)) + flip_pixbuf(window, FALSE); } static void @@ -1311,6 +1321,7 @@ static void vnr_window_cmd_open(GtkAction *action, VnrWindow *window) { GtkWidget *dialog; + GtkWidget *open_recursively_checkbox; GtkWidget *preview; GtkFileFilter *img_filter; GtkFileFilter *all_filter; @@ -1340,14 +1351,17 @@ vnr_window_cmd_open(GtkAction *action, VnrWindow *window) gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), img_filter); + open_recursively_checkbox = gtk_check_button_new_with_label(_("Include subfolders")); + gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), open_recursively_checkbox); + preview = gtk_image_new(); gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog), preview); g_signal_connect(GTK_FILE_CHOOSER(dialog), "update-preview", G_CALLBACK(update_preview_cb), preview); - if(window->file_list != NULL) + if(window->tree != NULL) { - gchar *dirname = g_path_get_dirname (VNR_FILE(window->file_list->data)->path); + gchar *dirname = g_path_get_dirname (VNR_FILE(window->tree->data)->path); gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(dialog), dirname); g_free(dirname); } @@ -1366,6 +1380,7 @@ static void vnr_window_cmd_open_dir(GtkAction *action, VnrWindow *window) { GtkWidget *dialog; + GtkWidget *open_recursively_checkbox; dialog = gtk_file_chooser_dialog_new (_("Open Folder"), GTK_WINDOW(window), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, @@ -1376,9 +1391,12 @@ vnr_window_cmd_open_dir(GtkAction *action, VnrWindow *window) gtk_window_set_modal (GTK_WINDOW(dialog), FALSE); gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); - if(window->file_list != NULL) + open_recursively_checkbox = gtk_check_button_new_with_label(_("Include subfolders")); + gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), open_recursively_checkbox); + + if(window->tree != NULL) { - gchar *dirname = g_path_get_dirname (VNR_FILE(window->file_list->data)->path); + gchar *dirname = g_path_get_dirname (VNR_FILE(window->tree->data)->path); gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(dialog), dirname); g_free(dirname); } @@ -1432,12 +1450,11 @@ vnr_window_cmd_about (GtkAction *action, VnrWindow *window) static void vnr_set_wallpaper(GtkAction *action, VnrWindow *win) { - pid_t pid; - - pid = fork(); + pid_t pid = fork(); if ( pid == 0 ) { - gchar * tmp; + gchar *tmp; + gchar *path = VNR_FILE(win->tree->data)->path; VnrPrefsDesktop desktop_environment = win->prefs->desktop; @@ -1449,63 +1466,63 @@ vnr_set_wallpaper(GtkAction *action, VnrWindow *win) switch(desktop_environment) { case VNR_PREFS_DESKTOP_GNOME2: execlp("gconftool-2", "gconftool-2", - "--set", "/desktop/gnome/background/picture_filename", - "--type", "string", - VNR_FILE(win->file_list->data)->path, - NULL); + "--set", "/desktop/gnome/background/picture_filename", + "--type", "string", + path, + NULL); break; case VNR_PREFS_DESKTOP_MATE: execlp("gsettings", "gsettings", - "set", "org.mate.background", - "picture-filename", VNR_FILE(win->file_list->data)->path, - NULL); + "set", "org.mate.background", + "picture-filename", path, + NULL); break; case VNR_PREFS_DESKTOP_GNOME3: - tmp = g_strdup_printf("file://%s", VNR_FILE(win->file_list->data)->path); + tmp = g_strdup_printf("file://%s", path); execlp("gsettings", "gsettings", - "set", "org.gnome.desktop.background", - "picture-uri", tmp, - NULL); + "set", "org.gnome.desktop.background", + "picture-uri", tmp, + NULL); break; case VNR_PREFS_DESKTOP_XFCE: tmp = g_strdup_printf("/backdrop/screen%d/monitor0/workspace0/last-image", - gdk_screen_get_number(gtk_widget_get_screen(GTK_WIDGET(win)))); + gdk_screen_get_number(gtk_widget_get_screen(GTK_WIDGET(win)))); execlp("xfconf-query", "xfconf-query", - "-c", "xfce4-desktop", - "-p", tmp, - "--type", "string", - "--set", - VNR_FILE(win->file_list->data)->path, - NULL); + "-c", "xfce4-desktop", + "-p", tmp, + "--type", "string", + "--set", + path, + NULL); break; case VNR_PREFS_DESKTOP_LXDE: execlp("pcmanfm", "pcmanfm", - "--set-wallpaper", - VNR_FILE(win->file_list->data)->path, - NULL); + "--set-wallpaper", + path, + NULL); break; case VNR_PREFS_DESKTOP_PUPPY: execlp("set_bg", "set_bg", - VNR_FILE(win->file_list->data)->path, - NULL); + path, + NULL); break; case VNR_PREFS_DESKTOP_FLUXBOX: execlp("fbsetbg", "fbsetbg", - "-f", VNR_FILE(win->file_list->data)->path, - NULL); + "-f", path, + NULL); break; case VNR_PREFS_DESKTOP_NITROGEN: execlp("nitrogen", "nitrogen", - "--set-zoom-fill", "--save", - VNR_FILE(win->file_list->data)->path, - NULL); + "--set-zoom-fill", "--save", + path, + NULL); break; case VNR_PREFS_DESKTOP_CINNAMON: - tmp = g_strdup_printf("file://%s", VNR_FILE(win->file_list->data)->path); + tmp = g_strdup_printf("file://%s", path); execlp("gsettings", "gsettings", - "set", "org.cinnamon.desktop.background", - "picture-uri", tmp, - NULL); + "set", "org.cinnamon.desktop.background", + "picture-uri", tmp, + NULL); break; default: _exit(0); @@ -1634,9 +1651,9 @@ vnr_window_cmd_delete(GtkAction *action, VnrWindow *window) if(window->fs_source != NULL) restart_autohide_timeout = TRUE; - g_return_if_fail (window->file_list != NULL); + g_return_if_fail (window->tree != NULL); - file_path = VNR_FILE(window->file_list->data)->path; + file_path = VNR_FILE(window->tree->data)->path; if(window->prefs->confirm_delete) { @@ -1645,7 +1662,7 @@ vnr_window_cmd_delete(GtkAction *action, VnrWindow *window) /* I18N: The '%s' is replaced with the name of the file to be deleted. */ prompt = g_strdup_printf (_("Are you sure you want to\n" "permanently delete \"%s\"?"), - VNR_FILE(window->file_list->data)->display_name); + VNR_FILE(window->tree->data)->display_name); markup = g_strdup_printf ("%s\n\n%s", prompt, warning); @@ -1665,7 +1682,7 @@ vnr_window_cmd_delete(GtkAction *action, VnrWindow *window) NULL); } - if(!window->prefs->confirm_delete || gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_YES ) + if(!window->prefs->confirm_delete || gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_YES) { GFile *file; GError *error = NULL; @@ -1676,23 +1693,21 @@ vnr_window_cmd_delete(GtkAction *action, VnrWindow *window) if( error != NULL ) { vnr_message_area_show(VNR_MESSAGE_AREA (window->msg_area), TRUE, - error->message, FALSE); + error->message, FALSE); restart_slideshow = FALSE; } else { - GList *next; - - next = g_list_next(window->file_list); - if(next == NULL) - next = g_list_first(window->file_list); + GNode *next; - if(g_list_length(g_list_first(window->file_list)) != 1) - window->file_list = g_list_delete_link (window->file_list, window->file_list); - else - { - g_list_free(window->file_list); + next = get_next_in_tree(window->tree); + if(get_total_number_of_leaves(window->tree) == 1) { + free_whole_tree(window->tree); next = NULL; + } else { + + g_node_unlink(window->tree); + free_current_tree(window->tree); } if(next == NULL) @@ -1700,7 +1715,7 @@ vnr_window_cmd_delete(GtkAction *action, VnrWindow *window) vnr_window_close(window); gtk_action_group_set_sensitive(window->actions_collection, FALSE); deny_slideshow(window); - vnr_window_set_list(window, NULL, FALSE); + vnr_window_set_tree(window, NULL, FALSE); vnr_message_area_show(VNR_MESSAGE_AREA (window->msg_area), TRUE, _("The given locations contain no images."), TRUE); @@ -1712,7 +1727,7 @@ vnr_window_cmd_delete(GtkAction *action, VnrWindow *window) } else { - vnr_window_set_list(window, next, FALSE); + vnr_window_set_tree(window, next, FALSE); if(window->prefs->confirm_delete && !window->cursor_is_hidden) gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(dlg)), gdk_cursor_new(GDK_WATCH)); @@ -1741,7 +1756,7 @@ vnr_window_cmd_delete(GtkAction *action, VnrWindow *window) { g_free(prompt); g_free(markup); - gtk_widget_destroy( dlg ); + gtk_widget_destroy(dlg); } } @@ -1803,12 +1818,12 @@ vnr_window_cmd_crop(GtkAction *action, VnrWindow *window) } static const GtkActionEntry action_entries_window[] = { - { "File", NULL, N_("_File") }, - { "Edit", NULL, N_("_Edit") }, - { "View", NULL, N_("_View") }, - { "Image", NULL, N_("_Image") }, - { "Go", NULL, N_("_Go") }, - { "Help", NULL, N_("_Help") }, + { "File", NULL, N_("_File") }, + { "Edit", NULL, N_("_Edit") }, + { "View", NULL, N_("_View") }, + { "Image", NULL, N_("_Image") }, + { "Go", NULL, N_("_Go") }, + { "Help", NULL, N_("_Help") }, { "FileOpen", GTK_STOCK_FILE, N_("Open _Image..."), "O", N_("Open an Image"), @@ -2079,7 +2094,7 @@ vnr_window_drag_data_received (GtkWidget *widget, return; } - vnr_window_open_from_list(VNR_WINDOW (widget), uri_list); + vnr_window_open_from_list(VNR_WINDOW (widget), uri_list, FALSE); } } @@ -2105,7 +2120,7 @@ vnr_window_init (VnrWindow * window) GtkAction *action; window->writable_format_name = NULL; - window->file_list = NULL; + window->tree = NULL; window->fs_controls = NULL; window->fs_source = NULL; window->ss_timeout = 5; @@ -2382,7 +2397,7 @@ vnr_window_init (VnrWindow * window) gtk_window_add_accel_group (GTK_WINDOW (window), gtk_ui_manager_get_accel_group (window->ui_mngr)); - vnr_window_load_accel_map(); + vnr_window_load_accel_map(); } /*************************************************************/ @@ -2396,11 +2411,12 @@ vnr_window_open (VnrWindow * window, gboolean fit_to_screen) GdkPixbufFormat *format; UniFittingMode last_fit_mode; GError *error = NULL; + gboolean is_static; - if(window->file_list == NULL) + if(window->tree == NULL) return FALSE; - file = VNR_FILE(window->file_list->data); + file = VNR_FILE(window->tree->data); update_fs_filename_label(window); @@ -2453,11 +2469,8 @@ vnr_window_open (VnrWindow * window, gboolean fit_to_screen) last_fit_mode = UNI_IMAGE_VIEW(window->view)->fitting; - /* Return TRUE if the image is static */ - if ( uni_anim_view_set_anim (UNI_ANIM_VIEW (window->view), pixbuf) ) - gtk_action_group_set_sensitive(window->actions_static_image, TRUE); - else - gtk_action_group_set_sensitive(window->actions_static_image, FALSE); + is_static = uni_anim_view_set_anim (UNI_ANIM_VIEW (window->view), pixbuf); + gtk_action_group_set_sensitive(window->actions_static_image, is_static); if(window->mode != VNR_WINDOW_MODE_NORMAL && window->prefs->fit_on_fullscreen) { @@ -2487,21 +2500,28 @@ vnr_window_open (VnrWindow * window, gboolean fit_to_screen) } void -vnr_window_open_from_list(VnrWindow *window, GSList *uri_list) +vnr_window_open_from_list(VnrWindow *window, GSList *uri_list, gboolean open_recursively) { - GList *file_list = NULL; + GNode *tree = NULL; GError *error = NULL; - if (g_slist_length(uri_list) == 1) - { - vnr_file_load_single_uri (uri_list->data, &file_list, window->prefs->show_hidden, &error); - } - else - { - vnr_file_load_uri_list (uri_list, &file_list, window->prefs->show_hidden, &error); + if (g_slist_length(uri_list) == 1) { + tree = create_tree_from_single_uri(uri_list->data, + window->prefs->show_hidden, + open_recursively, + tree_changed_callback, + window, + &error); + } else { + tree = create_tree_from_uri_list(uri_list, + window->prefs->show_hidden, + open_recursively, + tree_changed_callback, + window, + &error); } - if(error != NULL && file_list != NULL) + if(error != NULL && tree != NULL) { vnr_window_close(window); gtk_action_group_set_sensitive(window->actions_collection, FALSE); @@ -2509,7 +2529,7 @@ vnr_window_open_from_list(VnrWindow *window, GSList *uri_list) vnr_message_area_show(VNR_MESSAGE_AREA (window->msg_area), TRUE, error->message, TRUE); - vnr_window_set_list(window, file_list, TRUE); + vnr_window_set_tree(window, tree, TRUE); } else if(error != NULL) { @@ -2518,7 +2538,7 @@ vnr_window_open_from_list(VnrWindow *window, GSList *uri_list) vnr_message_area_show(VNR_MESSAGE_AREA (window->msg_area), TRUE, error->message, TRUE); } - else if(file_list == NULL) + else if(tree == NULL) { vnr_window_close(window); gtk_action_group_set_sensitive(window->actions_collection, FALSE); @@ -2529,7 +2549,7 @@ vnr_window_open_from_list(VnrWindow *window, GSList *uri_list) } else { - vnr_window_set_list(window, file_list, TRUE); + vnr_window_set_tree(window, tree, TRUE); if(!window->cursor_is_hidden) gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)), gdk_cursor_new(GDK_WATCH)); @@ -2555,11 +2575,11 @@ vnr_window_close(VnrWindow *window) } void -vnr_window_set_list (VnrWindow *window, GList *list, gboolean free_current) +vnr_window_set_tree (VnrWindow *window, GNode *tree, gboolean free_current) { - if (free_current == TRUE && window->file_list != NULL) - g_list_free (window->file_list); - if (g_list_length(g_list_first(list)) > 1) + if (free_current == TRUE && window->tree != NULL) + free_whole_tree(window->tree); + if (get_total_number_of_leaves(tree) > 1) { gtk_action_group_set_sensitive(window->actions_collection, TRUE); allow_slideshow(window); @@ -2569,28 +2589,21 @@ vnr_window_set_list (VnrWindow *window, GList *list, gboolean free_current) gtk_action_group_set_sensitive(window->actions_collection, FALSE); deny_slideshow(window); } - window->file_list = list; + window->tree = tree; } gboolean -vnr_window_next (VnrWindow *window, gboolean rem_timeout){ - GList *next; +vnr_window_next (VnrWindow *window, gboolean rem_timeout) { /* Don't reload current image * if the list contains only one (or no) image */ - if (g_list_length(g_list_first(window->file_list)) <2) + if (get_total_number_of_leaves(window->tree) <= 1) return FALSE; if(window->mode == VNR_WINDOW_MODE_SLIDESHOW && rem_timeout) g_source_remove (window->ss_source_tag); - next = g_list_next(window->file_list); - if(next == NULL) - { - next = g_list_first(window->file_list); - } - - window->file_list = next; + window->tree = get_next_in_tree(window->tree); if(!window->cursor_is_hidden) gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)), @@ -2612,24 +2625,17 @@ vnr_window_next (VnrWindow *window, gboolean rem_timeout){ } gboolean -vnr_window_prev (VnrWindow *window){ - GList *prev; +vnr_window_prev (VnrWindow *window) { /* Don't reload current image * if the list contains only one (or no) image */ - if (g_list_length(g_list_first(window->file_list)) <2) + if (get_total_number_of_leaves(window->tree) <= 1) return FALSE; if(window->mode == VNR_WINDOW_MODE_SLIDESHOW) g_source_remove (window->ss_source_tag); - prev = g_list_previous(window->file_list); - if(prev == NULL) - { - prev = g_list_last(window->file_list); - } - - window->file_list = prev; + window->tree = get_prev_in_tree(window->tree); if(!window->cursor_is_hidden) gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)), @@ -2652,16 +2658,13 @@ vnr_window_prev (VnrWindow *window){ gboolean vnr_window_first (VnrWindow *window){ - GList *prev; - - prev = g_list_first(window->file_list); if(vnr_message_area_is_critical(VNR_MESSAGE_AREA(window->msg_area))) { vnr_message_area_hide(VNR_MESSAGE_AREA(window->msg_area)); } - window->file_list = prev; + window->tree = get_first_in_tree(window->tree); if(!window->cursor_is_hidden) gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)), @@ -2678,16 +2681,13 @@ vnr_window_first (VnrWindow *window){ gboolean vnr_window_last (VnrWindow *window){ - GList *prev; - - prev = g_list_last(window->file_list); if(vnr_message_area_is_critical(VNR_MESSAGE_AREA(window->msg_area))) { vnr_message_area_hide(VNR_MESSAGE_AREA(window->msg_area)); } - window->file_list = prev; + window->tree = get_last_in_tree(window->tree); if(!window->cursor_is_hidden) gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window)), @@ -2726,12 +2726,26 @@ vnr_window_apply_preferences (VnrWindow *window) void vnr_window_toggle_fullscreen (VnrWindow *window) { - gboolean fullscreen; - - fullscreen = (window->mode == VNR_WINDOW_MODE_NORMAL)?TRUE:FALSE; + gboolean fullscreen = window->mode == VNR_WINDOW_MODE_NORMAL; if (fullscreen) vnr_window_fullscreen (window); else vnr_window_unfullscreen (window); } + +void tree_changed_callback(gboolean deleted, char* path, GNode *changed_node, GNode *root, gpointer data) { + VnrWindow *window = data; + + if(changed_node == window->tree && deleted) { + // The changed_node is the same as the currently opened file. This can only + // mean that the currently opened file was deleted. Go to the first file in + // the tree structure as fallback. + window->tree = root; + vnr_window_first(window); + } + + if(window != NULL) { + set_window_title(UNI_IMAGE_VIEW(window->view), window); + } +} diff --git a/src/vnr-window.h b/src/vnr-window.h index e70822b..b0a6c46 100755 --- a/src/vnr-window.h +++ b/src/vnr-window.h @@ -74,7 +74,7 @@ struct _VnrWindow { GtkWidget *view; GtkWidget *scroll_view; - GList *file_list; + GNode *tree; VnrPrefs *prefs; @@ -117,10 +117,10 @@ GtkWindow* vnr_window_new (void); /* Actions */ gboolean vnr_window_open (VnrWindow *win, gboolean fit_to_screen); -void vnr_window_open_from_list (VnrWindow *window, GSList *uri_list); +void vnr_window_open_from_list (VnrWindow *window, GSList *uri_list, gboolean open_recursively); void vnr_window_close (VnrWindow *win); -void vnr_window_set_list (VnrWindow *win, GList *list, gboolean free_current); +void vnr_window_set_tree (VnrWindow *win, GNode *tree, gboolean free_current); gboolean vnr_window_next (VnrWindow *win, gboolean rem_timeout); gboolean vnr_window_prev (VnrWindow *win); gboolean vnr_window_first (VnrWindow *win); @@ -129,5 +129,7 @@ void deny_slideshow (VnrWindow *window); void vnr_window_apply_preferences (VnrWindow *window); void vnr_window_toggle_fullscreen (VnrWindow *win); +void tree_changed_callback(gboolean deleted, char* path, GNode *changed_node, GNode *root, gpointer data); + G_END_DECLS #endif /* __VNR_WINDOW_H__ */ diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 0000000..1076054 --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,23 @@ +src_inc = include_directories('.') + +test_sources = [ + 'tree-printer.c', + 'utils.c', + '../src/vnr-file.c', + '../src/vnr-tree.c', +] + +test('tree-singlefile', executable('test-tree-singlefile', test_sources + ['test-tree-singlefile.c'], dependencies: viewnior_deps)) +test('tree-folder', executable('test-tree-folder', test_sources + ['test-tree-folder.c'], dependencies: viewnior_deps)) +test('tree-urilist', executable('test-tree-urilist', test_sources + ['test-tree-urilist.c'], dependencies: viewnior_deps)) +test('tree-next-nofiles', executable('test-tree-next-nofiles', test_sources + ['test-tree-next-nofiles.c'], dependencies: viewnior_deps)) +test('tree-next-iteration', executable('test-tree-next-iteration', test_sources + ['test-tree-next-iteration.c'], dependencies: viewnior_deps)) +test('tree-getchildindir', executable('test-tree-getchildindir', test_sources + ['test-tree-getchildindir.c'], dependencies: viewnior_deps)) +test('tree-addnode', executable('test-tree-addnode', test_sources + ['test-tree-addnode.c'], dependencies: viewnior_deps)) +test('tree-numberofleaves', executable('test-tree-numberofleaves', test_sources + ['test-tree-numberofleaves.c'], dependencies: viewnior_deps)) + +test('filemon-create', executable('test-filemon-create', test_sources + ['test-filemon-create.c'], dependencies: viewnior_deps)) +test('filemon-urilist-create', executable('test-filemon-urilist-create', test_sources + ['test-filemon-urilist-create.c'], dependencies: viewnior_deps)) +test('filemon-delete', executable('test-filemon-delete', test_sources + ['test-filemon-delete.c'], dependencies: viewnior_deps)) +test('filemon-urilist-delete', executable('test-filemon-urilist-delete', test_sources + ['test-filemon-urilist-delete.c'], dependencies: viewnior_deps)) +test('filemon-move', executable('test-filemon-move', test_sources + ['test-filemon-move.c'], dependencies: viewnior_deps)) diff --git a/tests/test-filemon-create.c b/tests/test-filemon-create.c new file mode 100644 index 0000000..7a4355f --- /dev/null +++ b/tests/test-filemon-create.c @@ -0,0 +1,511 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#include "utils.h" + + +static void test_filemonitor_createFileInFolder_noCallbackFunction() { + before(); + + GError *error = NULL; + monitor_test_tree = create_tree_from_single_uri(testdir_path, FALSE, FALSE, NULL, NULL, &error); + assert_error_is_null(error); + free(error); + + + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + assert_equals("File monitor before create in root with no callback ─ Include hidden files: F ─ Recursive: F", expected, output); + + + create_file(testdir_path, "/fepa.jpg"); + + char* expected_after = KWHT TESTDIRNAME RESET " (4 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +└─ fepa.jpg\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after create in root with no callback ─ Include hidden files: F ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(0); + + after(); +} + +static void test_filemonitor_createFileInFolder_nonRecursive() { + before(); + + monitor_test_tree = single_folder(FALSE, FALSE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + assert_equals("File monitor before create in root ─ Include hidden files: F ─ Recursive: F", expected, output); + + + create_file(testdir_path, "/fepa.jpg"); + + char* expected_after = KWHT TESTDIRNAME RESET " (4 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +└─ fepa.jpg\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after create in root ─ Include hidden files: F ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(1); + + after(); +} + +static void test_filemonitor_createFileInFolder_recursive() { + before(); + + monitor_test_tree = single_folder(FALSE, TRUE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + assert_equals("File monitor before create in subdir ─ Include hidden files: F ─ Recursive: T", expected, output); + + + create_file(testdir_path, "/dir_two/sub_dir_one/img3.png"); + + char* expected_after = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (4 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ ├─ img2.png\n\ + │ └─ img3.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after create in subdir ─ Include hidden files: F ─ Recursive: T", expected_after, output); + assert_file_system_changes_at_least(1); + + after(); +} + +static void test_filemonitor_createHiddenFileInFolder_nonRecursive() { + before(); + + monitor_test_tree = single_folder(FALSE, FALSE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + assert_equals("File monitor before create hidden in root ─ Include hidden files: F ─ Recursive: F", expected, output); + + + create_file(testdir_path, "/.epa.jpg"); + + char* expected_after = KWHT TESTDIRNAME RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after create hidden in root ─ Include hidden files: F ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(0); + + after(); +} + +static void test_filemonitor_createMultipleFilesInFolder_recursive() { + before(); + + monitor_test_tree = single_folder(FALSE, TRUE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + assert_equals("File monitor before create multiple files in subdirs ─ Include hidden files: F ─ Recursive: T", expected, output); + + + create_file(testdir_path, "/dir_two/sub_dir_one/img3.png"); + create_file(testdir_path, "/dir_two/depa.png"); + create_file(testdir_path, "/dir_two/sub_dir_four/subsub/img.png"); + create_file(testdir_path, "/apa.png"); + + char* expected_after = KWHT TESTDIRNAME RESET " (6 children)\n\ +├─ apa.png\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (8 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─ depa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├─┬" KWHT "subsub" RESET " (1 children)\n\ + │ │ └─ img.png\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (4 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ ├─ img2.png\n\ + │ └─ img3.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after create multiple files in subdirs ─ Include hidden files: F ─ Recursive: T", expected_after, output); + assert_file_system_changes_at_least(4); + + after(); +} + +static void test_filemonitor_createFileInFolder_sorted() { + before(); + + monitor_test_tree = single_folder(FALSE, FALSE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + assert_equals("File monitor before create file in folder sorted ─ Include hidden files: F ─ Recursive: F", expected, output); + + + create_file(testdir_path, "/bepb.png"); + + char* expected_after = KWHT TESTDIRNAME RESET " (4 children)\n\ +├─ bepa.png\n\ +├─ bepb.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after create file in folder sorted ─ Include hidden files: F ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(1); + + after(); +} + +static void test_filemonitor_createFolderInFolder_sorted() { + before(); + + monitor_test_tree = single_folder(FALSE, TRUE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + assert_equals("File monitor before create folder in folder sorted ─ Include hidden files: F ─ Recursive: T", expected, output); + + + create_dir (testdir_path, "/dir_onf"); + + char* expected_after = KWHT TESTDIRNAME RESET " (6 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +├──" KWHT "dir_onf" RESET " (0 children)\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after create folder in folder sorted ─ Include hidden files: F ─ Recursive: T", expected_after, output); + assert_file_system_changes_at_least(1); + + after(); +} + + +static void test_filemonitor_createDirThenCreateFilesInIt_recursive() { + before(); + + monitor_test_tree = single_folder(FALSE, TRUE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + assert_equals("File monitor before create dir with files ─ Include hidden files: F ─ Recursive: T", expected, output); + + + create_dir (testdir_path, "/dir_w00t"); + create_file(testdir_path, "/dir_w00t/apa.png"); + create_file(testdir_path, "/dir_w00t/bepa.png"); + + char* expected_after = KWHT TESTDIRNAME RESET " (6 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +├─┬" KWHT "dir_two" RESET " (7 children)\n\ +│ ├─ apa.png\n\ +│ ├─ bepa.png\n\ +│ ├─ cepa.png\n\ +│ ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ +│ │ ├──" KWHT "subsub" RESET " (0 children)\n\ +│ │ └──" KWHT "subsub2" RESET " (0 children)\n\ +│ ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ +│ │ ├─ img0.png\n\ +│ │ ├─ img1.png\n\ +│ │ └─ img2.png\n\ +│ ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ +│ └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ +│ ├─ img0.png\n\ +│ ├─ img1.png\n\ +│ ├─ img2.png\n\ +│ └─ img3.png\n\ +└─┬" KWHT "dir_w00t" RESET " (2 children)\n\ + ├─ apa.png\n\ + └─ bepa.png\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after create dir with files ─ Include hidden files: F ─ Recursive: T", expected_after, output); + assert_file_system_changes_at_least(1); // Files were added immediately after the the directory was created. + // Once the file monitor for the directory was setup, the directory + // may already have been populated. + + after(); +} + + +static void test_filemonitor_createDirThenCreateFilesInIt_nonRecursive() { + before(); + + monitor_test_tree = single_folder(FALSE, FALSE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + assert_equals("File monitor before create dir with files ─ Include hidden files: F ─ Recursive: F", expected, output); + + + create_dir (testdir_path, "/dir_three"); + create_file(testdir_path, "/dir_three/apa.png"); + create_file(testdir_path, "/dir_three/bepa.png"); + + char* expected_after = KWHT TESTDIRNAME RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after create dir with files ─ Include hidden files: F ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(0); // Not recursive -- directory ignored. + + after(); +} + + + +static gboolean file_monitor_tests(gpointer data) { + before_all(); + + test_filemonitor_createFileInFolder_noCallbackFunction(); + test_filemonitor_createFileInFolder_nonRecursive(); + test_filemonitor_createFileInFolder_recursive(); + test_filemonitor_createHiddenFileInFolder_nonRecursive(); + test_filemonitor_createMultipleFilesInFolder_recursive(); + test_filemonitor_createFileInFolder_sorted(); + test_filemonitor_createFolderInFolder_sorted(); + test_filemonitor_createDirThenCreateFilesInIt_recursive(); + test_filemonitor_createDirThenCreateFilesInIt_nonRecursive(); + + after_all(); + g_main_loop_quit((GMainLoop*)data); + return FALSE; +} + +int main() { + GMainLoop* loop = g_main_loop_new(NULL, FALSE); + g_timeout_add(1000, file_monitor_tests, loop); + g_main_loop_run(loop); + g_main_loop_unref(loop); + + return errors == 0 ? 0 : -1; +} diff --git a/tests/test-filemon-delete.c b/tests/test-filemon-delete.c new file mode 100644 index 0000000..2fc736b --- /dev/null +++ b/tests/test-filemon-delete.c @@ -0,0 +1,457 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#include "utils.h" + + +static void test_filemonitor_deleteFileInFolderRoot_noCallbackFunction() { + before(); + + GError *error = NULL; + monitor_test_tree = create_tree_from_single_uri(testdir_path, TRUE, FALSE, NULL, NULL, &error); + assert_error_is_null(error); + free(error); + + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ .apa.png\n\ +├─ .depa.gif\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + assert_equals("File monitor before delete in dirroot with no callback ─ Include hidden files: T ─ Recursive: F", expected, output); + + + remove_file(testdir_path, "/bepa.png"); + + char* expected_after = KWHT TESTDIRNAME RESET " (4 children)\n\ +├─ .apa.png\n\ +├─ .depa.gif\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after delete in dirroot with no callback ─ Include hidden files: T ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(0); + + after(); +} + +static void test_filemonitor_deleteFileInFolderRoot() { + before(); + + monitor_test_tree = single_folder(TRUE, FALSE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ .apa.png\n\ +├─ .depa.gif\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + assert_equals("File monitor before delete in dirroot ─ Include hidden files: T ─ Recursive: F", expected, output); + + + remove_file(testdir_path, "/bepa.png"); + + char* expected_after = KWHT TESTDIRNAME RESET " (4 children)\n\ +├─ .apa.png\n\ +├─ .depa.gif\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after delete in dirroot ─ Include hidden files: T ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(1); + + after(); +} + +static void test_filemonitor_deleteHiddenFileInFolderRoot() { + before(); + + monitor_test_tree = single_folder(TRUE, FALSE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ .apa.png\n\ +├─ .depa.gif\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + assert_equals("File monitor before delete hidden ─ Include hidden files: T ─ Recursive: F", expected, output); + + + remove_file(testdir_path, "/.apa.png"); + + char* expected_after = KWHT TESTDIRNAME RESET " (4 children)\n\ +├─ .depa.gif\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + wait_until_file_system_changes_is_as_expected(1); + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after delete hidden ─ Include hidden files: T ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(1); + + after(); +} + +static void test_filemonitor_deleteFileInSubFolder() { + before(); + + monitor_test_tree = single_folder(FALSE, TRUE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + assert_equals("File monitor before delete in subdir ─ Include hidden files: F ─ Recursive: T", expected, output); + + + remove_file(testdir_path, "/dir_two/sub_dir_one/img1.png"); + + char* expected_after = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (2 children)\n\ + │ ├─ img0.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after delete in subdir ─ Include hidden files: F ─ Recursive: T", expected_after, output); + assert_file_system_changes_at_least(1); + + after(); +} + +static void test_filemonitor_deleteMultipleFiles() { + before(); + + monitor_test_tree = single_folder(FALSE, TRUE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + assert_equals("File monitor before delete in subdir ─ Include hidden files: F ─ Recursive: T", expected, output); + + + remove_file(testdir_path, "/dir_two/sub_dir_one/img1.png"); + remove_file(testdir_path, "/dir_two/bepa.png"); + remove_file(testdir_path, "/dir_two/sub_dir_two/img2.png"); + + char* expected_after = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (6 children)\n\ + ├─ apa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (2 children)\n\ + │ ├─ img0.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (3 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + └─ img3.png\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after delete in subdir ─ Include hidden files: F ─ Recursive: T", expected_after, output); + assert_file_system_changes_at_least(3); + + after(); +} + +static void test_filemonitor_deleteSubsubFolder() { + before(); + + monitor_test_tree = single_folder(FALSE, TRUE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + assert_equals("File monitor before delete subsubdir ─ Include hidden files: F ─ Recursive: T", expected, output); + + + remove_directory(testdir_path, "/dir_two/sub_dir_one/"); + + char* expected_after = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (6 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + wait_until_file_system_changes_is_as_expected(5); + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + + assert_equals("File monitor after delete subsubdir ─ Include hidden files: F ─ Recursive: T", expected_after, output); + assert_file_system_changes_at_least(4); + + after(); +} + +static void test_filemonitor_deleteSubFolder() { + before(); + + monitor_test_tree = single_folder(FALSE, TRUE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + assert_equals("File monitor before delete subdir ─ Include hidden files: F ─ Recursive: T", expected, output); + + + remove_directory(testdir_path, "/dir_two/"); + + char* expected_after = KWHT TESTDIRNAME RESET " (4 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +└─┬" KWHT "dir_one" RESET " (1 children)\n\ + └─ two.jpg\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + + assert_equals("File monitor after delete subdir ─ Include hidden files: F ─ Recursive: T", expected_after, output); + assert_file_system_changes_at_least(16); + + after(); +} + +static void test_filemonitor_deleteRootFolder() { + before(); + + monitor_test_tree = single_folder(FALSE, TRUE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + assert_equals("File monitor before delete root ─ Include hidden files: F ─ Recursive: T", expected, output); + + + remove_directory(testdir_path, ""); + + + wait_until_tree_is_null(); + + assert_tree_is_null("File monitor after delete root ─ Include hidden files: F ─ Recursive: T", monitor_test_tree); + assert_file_system_changes_at_least(22); + + after(); +} + + + +static gboolean file_monitor_tests(gpointer data) { + before_all(); + + test_filemonitor_deleteFileInFolderRoot_noCallbackFunction(); + test_filemonitor_deleteFileInFolderRoot(); + test_filemonitor_deleteHiddenFileInFolderRoot(); + test_filemonitor_deleteFileInSubFolder(); + test_filemonitor_deleteMultipleFiles(); + test_filemonitor_deleteSubsubFolder(); + test_filemonitor_deleteSubFolder(); + test_filemonitor_deleteRootFolder(); + + after_all(); + g_main_loop_quit((GMainLoop*)data); + return FALSE; +} + +int main() { + GMainLoop* loop = g_main_loop_new(NULL, FALSE); + g_timeout_add(1000, file_monitor_tests, loop); + g_main_loop_run(loop); + g_main_loop_unref(loop); + + return errors == 0 ? 0 : -1; +} diff --git a/tests/test-filemon-move.c b/tests/test-filemon-move.c new file mode 100644 index 0000000..1da29be --- /dev/null +++ b/tests/test-filemon-move.c @@ -0,0 +1,354 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#include "utils.h" + + +static void test_filemonitor_moveFileInSameDir() { + before(); + + monitor_test_tree = single_folder(FALSE, TRUE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + assert_equals("File monitor before move file in same dir ─ Include hidden files: F ─ Recursive: T", expected, output); + + + char *path_src = append_strings(testdir_path, "/cepa.jpg"); + char *path_dst = append_strings(testdir_path, "/depa.jpg"); + + rename(path_src, path_dst); + + free(path_src); + free(path_dst); + + + char* expected_after = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ depa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + + assert_equals("File monitor after move file in same dir ─ Include hidden files: F ─ Recursive: T", expected_after, output); + assert_file_system_changes_at_least(2); + + after(); +} + +static void test_filemonitor_moveFileToSubdir() { + before(); + + monitor_test_tree = single_folder(FALSE, TRUE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + assert_equals("File monitor before move file to subdir ─ Include hidden files: F ─ Recursive: T", expected, output); + + + char *path_src = append_strings(testdir_path, "/cepa.jpg"); + char *path_dst = append_strings(testdir_path, "/dir_one/depa.jpg"); + + rename(path_src, path_dst); + + free(path_src); + free(path_dst); + + char* expected_after = KWHT TESTDIRNAME RESET " (4 children)\n\ +├─ bepa.png\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (2 children)\n\ +│ ├─ depa.jpg\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + + assert_equals("File monitor after move file to subdir ─ Include hidden files: F ─ Recursive: T", expected_after, output); + assert_file_system_changes_at_least(2); + + after(); +} + +static void test_filemonitor_moveFileToParentDir() { + before(); + + monitor_test_tree = single_folder(FALSE, TRUE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + assert_equals("File monitor before move file in subdir to root ─ Include hidden files: F ─ Recursive: T", expected, output); + + + char *path_src = append_strings(testdir_path, "/dir_two/apa.png"); + char *path_dst = append_strings(testdir_path, "/apa.png"); + + rename(path_src, path_dst); + + free(path_src); + free(path_dst); + + char* expected_after = KWHT TESTDIRNAME RESET " (6 children)\n\ +├─ apa.png\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (6 children)\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + + assert_equals("File monitor after move file in subdir to root ─ Include hidden files: F ─ Recursive: T", expected_after, output); + assert_file_system_changes_at_least(2); + + after(); +} + +static void test_filemonitor_moveDirInSameDir() { + before(); + + monitor_test_tree = single_folder(FALSE, TRUE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + assert_equals("File monitor before move dir in root ─ Include hidden files: F ─ Recursive: T", expected, output); + + + char *path_src = append_strings(testdir_path, "/dir_one"); + char *path_dst = append_strings(testdir_path, "/dir_moved"); + + rename(path_src, path_dst); + + free(path_src); + free(path_dst); + + char* expected_after = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_moved" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + + assert_equals("File monitor after move dir in root ─ Include hidden files: F ─ Recursive: T", expected_after, output); + assert_file_system_changes_at_least(2); + + after(); +} + + + +static gboolean file_monitor_tests(gpointer data) { + before_all(); + + test_filemonitor_moveFileInSameDir(); + test_filemonitor_moveFileToSubdir(); + test_filemonitor_moveFileToParentDir(); + test_filemonitor_moveDirInSameDir(); + + after_all(); + g_main_loop_quit((GMainLoop*)data); + return FALSE; +} + +int main() { + GMainLoop* loop = g_main_loop_new(NULL, FALSE); + g_timeout_add(1000, file_monitor_tests, loop); + g_main_loop_run(loop); + g_main_loop_unref(loop); + + return errors == 0 ? 0 : -1; +} diff --git a/tests/test-filemon-urilist-create.c b/tests/test-filemon-urilist-create.c new file mode 100644 index 0000000..e77f85c --- /dev/null +++ b/tests/test-filemon-urilist-create.c @@ -0,0 +1,227 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#include "utils.h" + + +static void test_filemonitor_uriList_createFileInRoot() { + before(); + + monitor_test_tree = uri_list(FALSE, FALSE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + + assert_equals("File monitor before urilist create file in root ─ Include hidden files: F ─ Recursive: F", expected, output); + + + create_file(testdir_path, "/apa.png"); + + char* expected_after = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after urilist create file in root ─ Include hidden files: F ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(0); + + after(); +} + + +static void test_filemonitor_uriList_createDirInRoot() { + before(); + + monitor_test_tree = uri_list(FALSE, FALSE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + + assert_equals("File monitor before urilist create dir in root ─ Include hidden files: F ─ Recursive: F", expected, output); + + + + create_dir (testdir_path, "/dir_three"); + create_file(testdir_path, "/dir_three/apa.png"); + create_file(testdir_path, "/dir_three/bepa.png"); + + char* expected_after = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after urilist create dir in root ─ Include hidden files: F ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(0); + + after(); +} + +static void test_filemonitor_uriList_createFileInWatchedDir() { + before(); + + monitor_test_tree = uri_list(FALSE, FALSE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + + assert_equals("File monitor before urilist create file in watched dir ─ Include hidden files: F ─ Recursive: F", expected, output); + + + + create_file(testdir_path, "/dir_two/depa.png"); + + char* expected_after = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (4 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + └─ depa.png\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after urilist create file in watched dir ─ Include hidden files: F ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(1); + + after(); +} + +static void test_filemonitor_uriList_createFileInNonwatchedDir() { + before(); + + monitor_test_tree = uri_list(FALSE, FALSE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + + assert_equals("File monitor before urilist create file in nonwatched dir ─ Include hidden files: F ─ Recursive: F", expected, output); + + + + create_file(testdir_path, "/dir_one/depa.png"); + + char* expected_after = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after urilist create file in nonwatched dir ─ Include hidden files: F ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(0); + + after(); +} + +static void test_filemonitor_uriList_createFileInEmptyRoot() { + before(); + + monitor_test_tree = uri_list_with_no_entries(FALSE, FALSE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT "" RESET " (0 children)\n\ +"; + + assert_equals("File monitor before urilist create file in empty dir ─ Include hidden files: F ─ Recursive: F", expected, output); + + + + create_file(testdir_path, "/depa.png"); + + char* expected_after = KWHT "" RESET " (0 children)\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after urilist create file in empty dir ─ Include hidden files: F ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(0); + + after(); +} + + + +static gboolean file_monitor_tests(gpointer data) { + before_all(); + + test_filemonitor_uriList_createFileInRoot(); + test_filemonitor_uriList_createDirInRoot(); + test_filemonitor_uriList_createFileInWatchedDir(); + test_filemonitor_uriList_createFileInNonwatchedDir(); + test_filemonitor_uriList_createFileInEmptyRoot(); + + after_all(); + g_main_loop_quit((GMainLoop*)data); + return FALSE; +} + +int main() { + GMainLoop* loop = g_main_loop_new(NULL, FALSE); + g_timeout_add(1000, file_monitor_tests, loop); + g_main_loop_run(loop); + g_main_loop_unref(loop); + + return errors == 0 ? 0 : -1; +} diff --git a/tests/test-filemon-urilist-delete.c b/tests/test-filemon-urilist-delete.c new file mode 100644 index 0000000..da6062f --- /dev/null +++ b/tests/test-filemon-urilist-delete.c @@ -0,0 +1,223 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#include "utils.h" + + +static void test_filemonitor_uriList_deleteFileUnderRoot() { + before(); + + monitor_test_tree = uri_list(FALSE, FALSE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + + assert_equals("File monitor before urilist delete file in root ─ Include hidden files: F ─ Recursive: F", expected, output); + + + + remove_file(testdir_path, "/bepa.png"); + + char* expected_after = KWHT "" RESET " (2 children)\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + + wait_until_file_system_changes_is_as_expected(1); + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after urilist delete file in root ─ Include hidden files: F ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(1); + + after(); +} + +static void test_filemonitor_uriList_deleteNonWatchedFileUnderRoot() { + before(); + + monitor_test_tree = uri_list(FALSE, FALSE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + + assert_equals("File monitor before urilist delete nonwatched file in root ─ Include hidden files: F ─ Recursive: F", expected, output); + + + + remove_file(testdir_path, "/epa.png"); + + char* expected_after = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after urilist delete nonwatched file in root ─ Include hidden files: F ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(0); + + after(); +} + +static void test_filemonitor_uriList_deleteNonWatchedFileUnderEmptyRoot() { + before(); + + monitor_test_tree = uri_list_with_no_entries(FALSE, FALSE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT "" RESET " (0 children)\n\ +"; + + assert_equals("File monitor before urilist delete file in empty root ─ Include hidden files: F ─ Recursive: F", expected, output); + + + + remove_file(testdir_path, "/epa.png"); + + char* expected_after = KWHT "" RESET " (0 children)\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after urilist delete file in empty root ─ Include hidden files: F ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(0); + + after(); +} + +static void test_filemonitor_uriList_deleteFileUnderWatchedDir() { + before(); + + monitor_test_tree = uri_list(FALSE, FALSE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + + assert_equals("File monitor before urilist delete file in watched dir ─ Include hidden files: F ─ Recursive: F", expected, output); + + + + remove_file(testdir_path, "/dir_two/bepa.png"); + + char* expected_after = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (2 children)\n\ + ├─ apa.png\n\ + └─ cepa.png\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after urilist delete file in watched dir ─ Include hidden files: F ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(1); + + after(); +} + +static void test_filemonitor_uriList_deleteFileUnderNonWatchedDir() { + before(); + + monitor_test_tree = uri_list(FALSE, FALSE); + pretty_print_tree(monitor_test_tree, output); + + char* expected = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + + assert_equals("File monitor before urilist delete file in nonwatched dir ─ Include hidden files: F ─ Recursive: F", expected, output); + + + + remove_file(testdir_path, "/dir_one/two.jpg"); + + char* expected_after = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + + wait_until_tree_is_as_expected(monitor_test_tree, expected_after); + + assert_equals("File monitor after urilist delete file in nonwatched dir ─ Include hidden files: F ─ Recursive: F", expected_after, output); + assert_file_system_changes_at_least(0); + + after(); +} + + + +static gboolean file_monitor_tests(gpointer data) { + before_all(); + + test_filemonitor_uriList_deleteFileUnderRoot(); + test_filemonitor_uriList_deleteNonWatchedFileUnderRoot(); + test_filemonitor_uriList_deleteNonWatchedFileUnderEmptyRoot(); + test_filemonitor_uriList_deleteFileUnderWatchedDir(); + test_filemonitor_uriList_deleteFileUnderNonWatchedDir(); + + after_all(); + g_main_loop_quit((GMainLoop*)data); + return FALSE; +} + +int main() { + GMainLoop* loop = g_main_loop_new(NULL, FALSE); + g_timeout_add(1000, file_monitor_tests, loop); + g_main_loop_run(loop); + g_main_loop_unref(loop); + + return errors == 0 ? 0 : -1; +} diff --git a/tests/test-tree-addnode.c b/tests/test-tree-addnode.c new file mode 100644 index 0000000..cfe6227 --- /dev/null +++ b/tests/test-tree-addnode.c @@ -0,0 +1,143 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#include "utils.h" + + +static void test_addNodeInTree_NullIn() { + before(); + + GNode *tree = NULL; + add_node_in_tree(tree, NULL); + assert_tree_is_null("Add node in tree ─ Tree is null", tree); + +// THIS IS THE STRUCTURE: +// +// test-dir (3 children) +//├─ bepa.png +//├─ cepa.jpg +//└─ epa.png + + GNode *expected = get_tree(SINGLE_FILE, FALSE, FALSE); + + add_node_in_tree(tree, expected); + assert_tree_is_null("Add node in tree ─ Tree is null", tree); + + tree = get_tree(SINGLE_FILE, FALSE, FALSE); + add_node_in_tree(tree, NULL); + assert_trees_equal("Add node in tree ─ Node is null", tree, expected); + + + GNode *node_without_data = g_node_new(NULL); + add_node_in_tree(tree, node_without_data); + assert_trees_equal("Add node in tree ─ Node is null", tree, expected); + + + free_whole_tree(tree); + free_whole_tree(expected); + free_whole_tree(node_without_data); + after(); +} + +static void test_addNodeInTree_TreesIn() { + before(); + + GNode *tree = get_tree(SINGLE_FILE, FALSE, FALSE); + GNode *node = get_tree(SINGLE_FILE, FALSE, FALSE); + tree = get_root_node(tree); + node = get_root_node(node); + + add_node_in_tree(tree, node); + char* expected = KWHT TESTDIRNAME RESET " (4 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +└─┬" KWHT TESTDIRNAME RESET " (3 children)\n\ + ├─ bepa.png\n\ + ├─ cepa.jpg\n\ + └─ epa.png\n\ +"; + + assert_equals("Add node in tree ─ Tree in tree: F ─ Recursive: F", expected, print_and_free_tree(tree)); + + after(); +} + +static void test_addNodeInTree_DuplicateNode() { + before(); + char *path = get_absolute_path(testdir_path, "/cepa.jpg"); + + VnrFile *vnrfile = vnr_file_create_new(path, "cepa.jpg", FALSE); + GNode *node = g_node_new(vnrfile); + + GNode *tree = get_tree(SINGLE_FILE, FALSE, FALSE); + tree = get_root_node(tree); + + add_node_in_tree(tree, node); + char* expected = KWHT TESTDIRNAME RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + assert_equals("Add node in tree ─ Duplicate node: F ─ Recursive: F", expected, print_and_free_tree(tree)); + + free(path); + free_whole_tree(node); + after(); +} + +static void test_addNodeInTree_TreeIsLeaf() { + before(); + char *path = append_strings(testdir_path, "/cepa.jpg"); + + VnrFile *vnrfile = vnr_file_create_new(path, "cepa.jpg", FALSE); + GNode *node = g_node_new(vnrfile); + + GNode *tree = get_tree(SINGLE_FILE, FALSE, FALSE); + tree = get_root_node(tree); + GNode *bepa = assert_forward_iteration(tree, "bepa.png"); + + add_node_in_tree(bepa, node); + char* expected = KWHT TESTDIRNAME RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + assert_equals("Add node in tree ─ Tree is leaf ─ No change", expected, print_and_free_tree(tree)); + + free(path); + free_whole_tree(node); + after(); +} + + + +int main() { + before_all(); + + test_addNodeInTree_NullIn(); + test_addNodeInTree_TreesIn(); + test_addNodeInTree_DuplicateNode(); + test_addNodeInTree_TreeIsLeaf(); + + after_all(); + return errors == 0 ? 0 : -1; +} diff --git a/tests/test-tree-folder.c b/tests/test-tree-folder.c new file mode 100644 index 0000000..4d82b61 --- /dev/null +++ b/tests/test-tree-folder.c @@ -0,0 +1,168 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#include "utils.h" + + +static void test_singleFolder_DontIncludeHidden_NotRecursive() { + before(); + + char* expected = KWHT TESTDIRNAME RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + GNode *tree = get_tree(SINGLE_FOLDER, FALSE, FALSE); + assert_numbers_equals("#Leaves Single folder ─ Include hidden files: F ─ Recursive: F", 3, get_total_number_of_leaves(tree)); + + assert_equals("Single folder ─ Include hidden files: F ─ Recursive: F", expected, print_and_free_tree(tree)); + + after(); +} + +static void test_singleFolder_DontIncludeHidden_Recursive() { + before(); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + GNode *tree = get_tree(SINGLE_FOLDER, FALSE, TRUE); + assert_numbers_equals("#Leaves Single folder ─ Include hidden files: F ─ Recursive: T", 14, get_total_number_of_leaves(tree)); + + assert_equals("Single folder ─ Include hidden files: F ─ Recursive: T", expected, print_and_free_tree(tree)); + + after(); +} + +static void test_singleFolder_IncludeHidden_NotRecursive() { + before(); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ .apa.png\n\ +├─ .depa.gif\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + GNode *tree = get_tree(SINGLE_FOLDER, TRUE, FALSE); + assert_numbers_equals("#Leaves Single folder ─ Include hidden files: T ─ Recursive: F", 5, get_total_number_of_leaves(tree)); + + assert_equals("Single folder ─ Include hidden files: T ─ Recursive: F", expected, print_and_free_tree(tree)); + + after(); +} + +static void test_singleFolder_IncludeHidden_Recursive() { + before(); + + char* expected = KWHT TESTDIRNAME RESET " (7 children)\n\ +├─ .apa.png\n\ +├─ .depa.gif\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (3 children)\n\ +│ ├─ .three.png\n\ +│ ├─ two.jpg\n\ +│ └─┬" KWHT ".secrets" RESET " (1 children)\n\ +│ └─ img.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + GNode *tree = get_tree(SINGLE_FOLDER, TRUE, TRUE); + assert_numbers_equals("#Leaves Single folder ─ Include hidden files: T ─ Recursive: T", 18, get_total_number_of_leaves(tree)); + + assert_equals("Single folder ─ Include hidden files: T ─ Recursive: T", expected, print_and_free_tree(tree)); + + after(); +} + + +static void assert_node_has_path(char* path, gboolean include_hidden, gboolean recursive) { + GNode *tree = open_single_file(testdir_path, include_hidden, recursive); + assert_equals("Node returned is the first in folder ─ Include hidden files: T ─ Recursive: T", path, ((VnrFile*) (tree->data))->path); + free_whole_tree(tree); +} + +static void test_singleFolder_nodeHasTheRequestedPath() { + before(); + char *path_normal = get_absolute_path(testdir_path, "/bepa.png"); + char *path_hidden = get_absolute_path(testdir_path, "/.apa.png"); + + assert_node_has_path(path_normal, FALSE, FALSE); + assert_node_has_path(path_normal, FALSE, TRUE); + assert_node_has_path(path_hidden, TRUE, FALSE); + assert_node_has_path(path_hidden, TRUE, TRUE); + + free(path_normal); + free(path_hidden); + after(); +} + + + +int main() { + before_all(); + + test_singleFolder_DontIncludeHidden_NotRecursive(); + test_singleFolder_DontIncludeHidden_Recursive(); + test_singleFolder_IncludeHidden_NotRecursive(); + test_singleFolder_IncludeHidden_Recursive(); + test_singleFolder_nodeHasTheRequestedPath(); + + after_all(); + return errors == 0 ? 0 : -1; +} diff --git a/tests/test-tree-getchildindir.c b/tests/test-tree-getchildindir.c new file mode 100644 index 0000000..27fb9b5 --- /dev/null +++ b/tests/test-tree-getchildindir.c @@ -0,0 +1,426 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#include "utils.h" + + +static void assert_child_is_equal(char* description, GNode* tree, char* expected) { + VnrFile *vnrfile = tree != NULL ? tree->data : NULL; + assert_equals(description, expected, vnrfile != NULL ? vnrfile->path : "NULL"); +} + + +static void test_getChildInDirectory_FindFromRoot_FileExists() { + before(); + GNode *tree = single_folder(TRUE, TRUE); + +// THIS IS THE STRUCTURE: +// +// test-dir (7 children) +// ├─ .apa.png +// ├─ .depa.gif +// ├─ bepa.png +// ├─ cepa.jpg +// ├─ epa.png +// ├─┬dir_one (3 children) +// │ ├─ .three.png +// │ ├─ two.jpg +// │ └─┬.secrets (1 children) +// │ └─ img.jpg +// └─┬dir_two (7 children) +// ├─ apa.png +// ├─ bepa.png +// ├─ cepa.png +// ├─┬sub_dir_four (2 children) +// │ ├──subsub (0 children) +// │ └──subsub2 (0 children) +// ├─┬sub_dir_one (3 children) +// │ ├─ img0.png +// │ ├─ img1.png +// │ └─ img2.png +// ├──sub_dir_three (0 children) +// └─┬sub_dir_two (3 children) +// ├─ img0.png +// ├─ img1.png +// ├─ img2.png +// └─ img3.png + + char *path0 = get_absolute_path(testdir_path, "/bepa.png"); + char *path1 = get_absolute_path(testdir_path, "/dir_two/bepa.png"); + + GNode *child0 = get_child_in_directory(tree, path0); + GNode *child1 = get_child_in_directory(tree, path1); + + assert_child_is_equal("Get child in directory ─ From root ─ File exists", child0, path0); + assert_child_is_equal("Get child in directory ─ From root ─ File exists", child1, path1); + + free(path0); + free(path1); + free_whole_tree(tree); + after(); +} + +static void test_getChildInDirectory_FindFromFileAndDir_FileExists() { + before(); + GNode *tree = single_folder(TRUE, TRUE); + +// THIS IS THE STRUCTURE: +// +// test-dir (7 children) +// ├─ .apa.png +// ├─ .depa.gif +// ├─ bepa.png +// ├─ cepa.jpg +// ├─ epa.png +// ├─┬dir_one (3 children) +// │ ├─ .three.png +// │ ├─ two.jpg +// │ └─┬.secrets (1 children) +// │ └─ img.jpg +// └─┬dir_two (7 children) +// ├─ apa.png +// ├─ bepa.png +// ├─ cepa.png +// ├─┬sub_dir_four (2 children) +// │ ├──subsub (0 children) +// │ └──subsub2 (0 children) +// ├─┬sub_dir_one (3 children) +// │ ├─ img0.png +// │ ├─ img1.png +// │ └─ img2.png +// ├──sub_dir_three (0 children) +// └─┬sub_dir_two (3 children) +// ├─ img0.png +// ├─ img1.png +// ├─ img2.png +// └─ img3.png + + char *path0 = get_absolute_path(testdir_path, "/bepa.png"); + char *path1 = get_absolute_path(testdir_path, "/dir_two"); + char *path2 = get_absolute_path(testdir_path, "/dir_two/bepa.png"); + + GNode *child0 = get_child_in_directory(tree, path0); + GNode *child1 = g_node_last_child(get_root_node(tree)); + assert_child_is_equal("Get child in directory ─ Find bepa.png", child0, path0); + assert_child_is_equal("Get child in directory ─ Last child is dir_two", child1, path1); + + GNode *child2 = get_child_in_directory(child0, path2); + GNode *child3 = get_child_in_directory(child1, path2); + + assert_child_is_equal("Get child in directory ─ From file ─ File exists", child2, path2); + assert_child_is_equal("Get child in directory ─ From subdir ─ File exists", child3, path2); + + free(path0); + free(path1); + free(path2); + free_whole_tree(tree); + after(); +} + +static void test_getChildInDirectory_FindAbove_FileExists() { + before(); + GNode *tree = single_folder(TRUE, TRUE); + +// THIS IS THE STRUCTURE: +// +// test-dir (7 children) +// ├─ .apa.png +// ├─ .depa.gif +// ├─ bepa.png +// ├─ cepa.jpg +// ├─ epa.png +// ├─┬dir_one (3 children) +// │ ├─ .three.png +// │ ├─ two.jpg +// │ └─┬.secrets (1 children) +// │ └─ img.jpg +// └─┬dir_two (7 children) +// ├─ apa.png +// ├─ bepa.png +// ├─ cepa.png +// ├─┬sub_dir_four (2 children) +// │ ├──subsub (0 children) +// │ └──subsub2 (0 children) +// ├─┬sub_dir_one (3 children) +// │ ├─ img0.png +// │ ├─ img1.png +// │ └─ img2.png +// ├──sub_dir_three (0 children) +// └─┬sub_dir_two (3 children) +// ├─ img0.png +// ├─ img1.png +// ├─ img2.png +// └─ img3.png + + char *path0 = get_absolute_path(testdir_path, "/dir_two"); + char *path1 = get_absolute_path(testdir_path, "/epa.png"); + + GNode *child0 = g_node_last_child(get_root_node(tree)); + assert_child_is_equal("Get child in directory ─ Find above ─ Last child is dir_two", child0, path0); + + GNode *child1 = get_child_in_directory(child0, path1); + + assert_child_is_equal("Get child in directory ─ Find above ─ File exists", child1, path1); + + free(path0); + free(path1); + free_whole_tree(tree); + after(); +} + +static void test_getChildInDirectory_FileDoesNotExist() { + before(); + GNode *tree = single_folder(TRUE, TRUE); + +// THIS IS THE STRUCTURE: +// +// test-dir (7 children) +// ├─ .apa.png +// ├─ .depa.gif +// ├─ bepa.png +// ├─ cepa.jpg +// ├─ epa.png +// ├─┬dir_one (3 children) +// │ ├─ .three.png +// │ ├─ two.jpg +// │ └─┬.secrets (1 children) +// │ └─ img.jpg +// └─┬dir_two (7 children) +// ├─ apa.png +// ├─ bepa.png +// ├─ cepa.png +// ├─┬sub_dir_four (2 children) +// │ ├──subsub (0 children) +// │ └──subsub2 (0 children) +// ├─┬sub_dir_one (3 children) +// │ ├─ img0.png +// │ ├─ img1.png +// │ └─ img2.png +// ├──sub_dir_three (0 children) +// └─┬sub_dir_two (3 children) +// ├─ img0.png +// ├─ img1.png +// ├─ img2.png +// └─ img3.png + + char *path0 = get_absolute_path(testdir_path, "/dir_two"); + char *path1 = get_absolute_path(testdir_path, "/dir_two/depa.png"); + + GNode *child = g_node_last_child(get_root_node(tree)); + assert_child_is_equal("Get child in directory ─ File does not exist ─ Last child is dir_two", child, path0); + + child = get_child_in_directory(child, path1); + + assert_tree_is_null("Get child in directory ─ File does not exist", child); + + free(path0); + free(path1); + free_whole_tree(tree); + after(); +} + +static void test_getChildInDirectory_SearchFromSubDirectoryIsEmpty() { + before(); + GNode *tree = single_folder(TRUE, TRUE); + +// THIS IS THE STRUCTURE: +// +// test-dir (7 children) +// ├─ .apa.png +// ├─ .depa.gif +// ├─ bepa.png +// ├─ cepa.jpg +// ├─ epa.png +// ├─┬dir_one (3 children) +// │ ├─ .three.png +// │ ├─ two.jpg +// │ └─┬.secrets (1 children) +// │ └─ img.jpg +// └─┬dir_two (7 children) +// ├─ apa.png +// ├─ bepa.png +// ├─ cepa.png +// ├─┬sub_dir_four (2 children) +// │ ├──subsub (0 children) +// │ └──subsub2 (0 children) +// ├─┬sub_dir_one (3 children) +// │ ├─ img0.png +// │ ├─ img1.png +// │ └─ img2.png +// ├──sub_dir_three (0 children) +// └─┬sub_dir_two (3 children) +// ├─ img0.png +// ├─ img1.png +// ├─ img2.png +// └─ img3.png + + char *path0 = get_absolute_path(testdir_path, "/dir_two/sub_dir_four/subsub"); + char *path1 = get_absolute_path(testdir_path, "/.apa.png"); + + GNode *child = get_root_node(tree); + child = g_node_last_child(child); + child = g_node_first_child(child); + child = g_node_next_sibling(child); + child = g_node_next_sibling(child); + child = g_node_next_sibling(child); + child = g_node_first_child(child); + assert_child_is_equal("Get child in directory ─ From subdir ─ Last child is subsub", child, path0); + + child = get_child_in_directory(child, path1); + + assert_child_is_equal("Get child in directory ─ From subdir ─ File exists", child, path1); + + free(path0); + free(path1); + free_whole_tree(tree); + after(); +} + +static void test_getChildInDirectory_DirectoryIsEmpty() { + before(); + GNode *tree = single_folder(TRUE, TRUE); + +// THIS IS THE STRUCTURE: +// +// test-dir (7 children) +// ├─ .apa.png +// ├─ .depa.gif +// ├─ bepa.png +// ├─ cepa.jpg +// ├─ epa.png +// ├─┬dir_one (3 children) +// │ ├─ .three.png +// │ ├─ two.jpg +// │ └─┬.secrets (1 children) +// │ └─ img.jpg +// └─┬dir_two (7 children) +// ├─ apa.png +// ├─ bepa.png +// ├─ cepa.png +// ├─┬sub_dir_four (2 children) +// │ ├──subsub (0 children) +// │ └──subsub2 (0 children) +// ├─┬sub_dir_one (3 children) +// │ ├─ img0.png +// │ ├─ img1.png +// │ └─ img2.png +// ├──sub_dir_three (0 children) +// └─┬sub_dir_two (3 children) +// ├─ img0.png +// ├─ img1.png +// ├─ img2.png +// └─ img3.png + + char *path0 = get_absolute_path(testdir_path, "/dir_two/sub_dir_four/subsub"); + char *path1 = get_absolute_path(testdir_path, "/dir_two/sub_dir_four/subsub/apa.jpg"); + + GNode *child = get_root_node(tree); + child = g_node_last_child(child); + child = g_node_first_child(child); + child = g_node_next_sibling(child); + child = g_node_next_sibling(child); + child = g_node_next_sibling(child); + child = g_node_first_child(child); + assert_child_is_equal("Get child in directory ─ Last child is subsub", child, path0); + + child = get_child_in_directory(child, path1); + + assert_tree_is_null("Get child in directory ─ Directory is empty", child); + + free(path0); + free(path1); + free_whole_tree(tree); + after(); +} + +static void test_getChildInDirectory_FindRoot() { + before(); + GNode *tree = single_folder(TRUE, TRUE); + +// THIS IS THE STRUCTURE: +// +// test-dir (7 children) +// ├─ .apa.png +// ├─ .depa.gif +// ├─ bepa.png +// ├─ cepa.jpg +// ├─ epa.png +// ├─┬dir_one (3 children) +// │ ├─ .three.png +// │ ├─ two.jpg +// │ └─┬.secrets (1 children) +// │ └─ img.jpg +// └─┬dir_two (7 children) +// ├─ apa.png +// ├─ bepa.png +// ├─ cepa.png +// ├─┬sub_dir_four (2 children) +// │ ├──subsub (0 children) +// │ └──subsub2 (0 children) +// ├─┬sub_dir_one (3 children) +// │ ├─ img0.png +// │ ├─ img1.png +// │ └─ img2.png +// ├──sub_dir_three (0 children) +// └─┬sub_dir_two (3 children) +// ├─ img0.png +// ├─ img1.png +// ├─ img2.png +// └─ img3.png + + char *path0 = get_absolute_path(testdir_path, ""); + + GNode *child = get_child_in_directory(tree, path0); + + assert_child_is_equal("Get child in directory ─ Find root", child, path0); + + free(path0); + free_whole_tree(tree); + after(); +} + +static void test_getChildInDirectory_DirectoryIsNull() { + before(); + char *path0 = get_absolute_path(testdir_path, "/dir_two/sub_dir_four/subsub/apa.jpg"); + + GNode *child = get_child_in_directory(NULL, path0); + + assert_tree_is_null("Get child in directory ─ Directory is empty", child); + + free(path0); + after(); +} + + + +int main() { + before_all(); + + test_getChildInDirectory_FindFromRoot_FileExists(); + test_getChildInDirectory_FindFromFileAndDir_FileExists(); + test_getChildInDirectory_FindAbove_FileExists(); + test_getChildInDirectory_FileDoesNotExist(); + test_getChildInDirectory_SearchFromSubDirectoryIsEmpty(); + test_getChildInDirectory_DirectoryIsEmpty(); + test_getChildInDirectory_FindRoot(); + test_getChildInDirectory_DirectoryIsNull(); + + after_all(); + return errors == 0 ? 0 : -1; +} diff --git a/tests/test-tree-next-iteration.c b/tests/test-tree-next-iteration.c new file mode 100644 index 0000000..a1bb783 --- /dev/null +++ b/tests/test-tree-next-iteration.c @@ -0,0 +1,568 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#include "utils.h" + + +static void test_getNextInTree_RootIn() { + before(); + + GNode *tree = single_file(TRUE, FALSE); + tree = get_root_node(tree); + assert_forward_iteration(tree, ".apa.png"); + assert_equals("Get First ─ Root in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Root in", "epa.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + free_whole_tree(tree); + + after(); +} + +static void test_getNextInTree_FolderIn() { + before(); + + GNode *tree = single_file(TRUE, TRUE); + tree = get_root_node(tree); + + tree = assert_forward_iteration(tree, ".apa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + tree = assert_forward_iteration(tree, ".depa.gif"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + tree = assert_forward_iteration(tree, "bepa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + tree = assert_forward_iteration(tree, "cepa.jpg"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + tree = assert_forward_iteration(tree, "epa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = g_node_next_sibling(tree); + VnrFile* vnrfile = tree->data; + assert_equals("First directory should be dir_one", "dir_one", vnrfile->display_name); + + tree = assert_forward_iteration(tree, ".three.png"); + tree = assert_forward_iteration(tree, "two.jpg"); + tree = assert_forward_iteration(tree, "img.jpg"); + tree = assert_forward_iteration(tree, "apa.png"); + tree = assert_forward_iteration(tree, "bepa.png"); + tree = assert_forward_iteration(tree, "cepa.png"); + + GNode *sub_dir_four = g_node_next_sibling(tree); + vnrfile = sub_dir_four->data; + assert_equals("Directory should be sub_dir_four", "sub_dir_four", vnrfile->display_name); + + GNode *subsub = g_node_first_child(sub_dir_four); + vnrfile = subsub->data; + assert_equals("Directory should be subsub", "subsub", vnrfile->display_name); + + assert_forward_iteration(subsub, "img0.png"); + + GNode *subsub2 = g_node_next_sibling(subsub); + vnrfile = subsub2->data; + assert_equals("Directory should be subsub2", "subsub2", vnrfile->display_name); + + assert_forward_iteration(subsub2, "img0.png"); + + free_whole_tree(tree); + + after(); +} + + +static void test_getNextInTree_Iterate() { + before(); + + GNode *tree = single_file(TRUE, TRUE); + +// THIS IS THE STRUCTURE: +// +// test-dir (7 children) +// ├─ .apa.png +// ├─ .depa.gif +// ├─ bepa.png +// ├─ cepa.jpg +// ├─ epa.png +// ├─┬dir_one (3 children) +// │ ├─ .three.png +// │ ├─ two.jpg +// │ └─┬.secrets (1 children) +// │ └─ img.jpg +// └─┬dir_two (7 children) +// ├─ apa.png +// ├─ bepa.png +// ├─ cepa.png +// ├─┬sub_dir_four (2 children) +// │ ├──subsub (0 children) +// │ └──subsub2 (0 children) +// ├─┬sub_dir_one (3 children) +// │ ├─ img0.png +// │ ├─ img1.png +// │ └─ img2.png +// ├──sub_dir_three (0 children) +// └─┬sub_dir_two (3 children) +// ├─ img0.png +// ├─ img1.png +// ├─ img2.png +// └─ img3.png + + tree = get_root_node(tree); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, ".apa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, ".depa.gif"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "bepa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "cepa.jpg"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "epa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, ".three.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "two.jpg"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "img.jpg"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "apa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "bepa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "cepa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "img0.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "img1.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "img2.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "img0.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "img1.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "img2.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "img3.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + // Loop from the beginning. + tree = assert_forward_iteration(tree, ".apa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, ".depa.gif"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + free_whole_tree(tree); + + after(); +} + +static void test_getPrevInTree_Iterate() { + before(); + + GNode *tree = single_file(TRUE, TRUE); + +// THIS IS THE STRUCTURE: +// +// test-dir (7 children) +// ├─ .apa.png +// ├─ .depa.gif +// ├─ bepa.png +// ├─ cepa.jpg +// ├─ epa.png +// ├─┬dir_one (3 children) +// │ ├─ .three.png +// │ ├─ two.jpg +// │ └─┬.secrets (1 children) +// │ └─ img.jpg +// └─┬dir_two (7 children) +// ├─ apa.png +// ├─ bepa.png +// ├─ cepa.png +// ├─┬sub_dir_four (2 children) +// │ ├──subsub (0 children) +// │ └──subsub2 (0 children) +// ├─┬sub_dir_one (3 children) +// │ ├─ img0.png +// │ ├─ img1.png +// │ └─ img2.png +// ├──sub_dir_three (0 children) +// └─┬sub_dir_two (3 children) +// ├─ img0.png +// ├─ img1.png +// ├─ img2.png +// └─ img3.png + + tree = get_root_node(tree); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img3.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img2.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img1.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img0.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img2.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img1.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img0.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "cepa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "bepa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "apa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img.jpg"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "two.jpg"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, ".three.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "epa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "cepa.jpg"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "bepa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, ".depa.gif"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, ".apa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + // Loop from the end. + tree = assert_backward_iteration(tree, "img3.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img2.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + + free_whole_tree(tree); + + after(); +} + +static void test_getNextInTree_UriList_Iterate() { + before(); + + GNode *tree = uri_list(TRUE, TRUE); + +// THIS IS THE STRUCTURE: +// +// (5 children) +// ├─ .apa.png +// ├─ .depa.gif +// ├─ bepa.png +// ├─ cepa.jpg +// └─┬dir_two (7 children) +// ├─ apa.png +// ├─ bepa.png +// ├─ cepa.png +// ├─┬sub_dir_four (2 children) +// │ ├──subsub (0 children) +// │ └──subsub2 (0 children) +// ├─┬sub_dir_one (3 children) +// │ ├─ img0.png +// │ ├─ img1.png +// │ └─ img2.png +// ├──sub_dir_three (0 children) +// └─┬sub_dir_two (4 children) +// ├─ img0.png +// ├─ img1.png +// ├─ img2.png +// └─ img3.png + + tree = get_root_node(tree); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, ".apa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, ".depa.gif"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "bepa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "cepa.jpg"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "apa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "bepa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "cepa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "img0.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "img1.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "img2.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "img0.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "img1.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "img2.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, "img3.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + // Loop from the beginning. + tree = assert_forward_iteration(tree, ".apa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_forward_iteration(tree, ".depa.gif"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + + free_whole_tree(tree); + + after(); +} + +static void test_getPrevInTree_UriList_Iterate() { + before(); + + GNode *tree = uri_list(TRUE, TRUE); + +// THIS IS THE STRUCTURE: +// +// (5 children) +// ├─ .apa.png +// ├─ .depa.gif +// ├─ bepa.png +// ├─ cepa.jpg +// └─┬dir_two (7 children) +// ├─ apa.png +// ├─ bepa.png +// ├─ cepa.png +// ├─┬sub_dir_four (2 children) +// │ ├──subsub (0 children) +// │ └──subsub2 (0 children) +// ├─┬sub_dir_one (3 children) +// │ ├─ img0.png +// │ ├─ img1.png +// │ └─ img2.png +// ├──sub_dir_three (0 children) +// └─┬sub_dir_two (4 children) +// ├─ img0.png +// ├─ img1.png +// ├─ img2.png +// └─ img3.png + + tree = get_root_node(tree); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img3.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img2.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img1.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img0.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img2.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img1.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img0.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "cepa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "bepa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "apa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "cepa.jpg"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "bepa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, ".depa.gif"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, ".apa.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + // Loop from the end. + tree = assert_backward_iteration(tree, "img3.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + tree = assert_backward_iteration(tree, "img2.png"); + assert_equals("Get First ─ Folder in", ".apa.png", (VNR_FILE(get_first_in_tree(tree)->data)->display_name)); + assert_equals("Get Last ─ Folder in", "img3.png", (VNR_FILE(get_last_in_tree (tree)->data)->display_name)); + + + free_whole_tree(tree); + + after(); +} + + + +int main() { + before_all(); + + test_getNextInTree_RootIn(); + test_getNextInTree_FolderIn(); + test_getNextInTree_Iterate(); + test_getPrevInTree_Iterate(); + test_getNextInTree_UriList_Iterate(); + test_getPrevInTree_UriList_Iterate(); + + after_all(); + return errors == 0 ? 0 : -1; +} diff --git a/tests/test-tree-next-nofiles.c b/tests/test-tree-next-nofiles.c new file mode 100644 index 0000000..ae6783a --- /dev/null +++ b/tests/test-tree-next-nofiles.c @@ -0,0 +1,113 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#include "utils.h" + + +static void test_getNextInTree_NullIn() { + before(); + assert_tree_is_null("Get Next ─ Input is NULL", get_next_in_tree(NULL)); + assert_tree_is_null("Get First ─ Input is NULL", get_first_in_tree(NULL)); + assert_tree_is_null("Get Last ─ Input is NULL", get_last_in_tree(NULL)); + after(); +} + +static void test_getNextInTree_RootWithoutElements() { + before(); + + GNode *tree = g_node_new(NULL); + assert_trees_equal("Get Next ─ Input is Root without elements", tree, get_next_in_tree(tree)); + assert_trees_equal("Get First ─ Input is Root without elements", tree, get_first_in_tree(tree)); + assert_trees_equal("Get Last ─ Input is Root without elements", tree, get_last_in_tree(tree)); + free_whole_tree(tree); + + after(); +} + +static void test_getNextInTree_RootWithOnlyDir() { + before(); + + VnrFile* vnrfile = vnr_file_create_new("filepath", "display_name", TRUE); + GNode *tree = g_node_new(vnrfile); + assert_trees_equal("Get Next ─ Input is Root with only one dir", tree, get_next_in_tree(tree)); + assert_trees_equal("Get First ─ Input is Root with only one dir", tree, get_first_in_tree(tree)); + assert_trees_equal("Get Last ─ Input is Root with only one dir", tree, get_last_in_tree(tree)); + free_whole_tree(tree); + + after(); +} + +static void test_getNextInTree_SingleFolder_RootWithOnlyThreeDirs() { + // No before! + + create_dir(testdir_path, ""); + create_dir(testdir_path, "/apa"); + create_dir(testdir_path, "/bepa"); + create_dir(testdir_path, "/cepa"); + + GNode *tree = single_folder(TRUE, TRUE); + + assert_trees_equal("Get Next ─ Single folder ─ Input is Root with only three dirs", tree, get_next_in_tree(tree)); + assert_trees_equal("Get First ─ Single folder ─ Input is Root with only three dirs", tree, get_first_in_tree(tree)); + assert_trees_equal("Get Last ─ Single folder ─ Input is Root with only three dirs", tree, get_last_in_tree(tree)); + free_whole_tree(tree); + + after(); +} + +static void test_getNextInTree_UriList_RootWithOnlyThreeDirs() { + // No before! + + create_dir(testdir_path, ""); + create_dir(testdir_path, "/apa"); + create_dir(testdir_path, "/bepa"); + create_dir(testdir_path, "/cepa"); + + GSList *uri_list = NULL; + uri_list = add_path_to_list(uri_list, testdir_path, "/bepa.png"); + uri_list = add_path_to_list(uri_list, testdir_path, "/cepa.jpg"); + + GError *error = NULL; + GNode *tree = create_tree_from_uri_list(uri_list, TRUE, TRUE, NULL, NULL, &error); + assert_error_is_null(error); + free(error); + g_slist_free_full(uri_list, free); + + assert_trees_equal("Get Next ─ Uri List ─ Input is Root with only three dirs", tree, get_next_in_tree(tree)); + assert_trees_equal("Get First ─ Uri List ─ Input is Root with only three dirs", tree, get_first_in_tree(tree)); + assert_trees_equal("Get Last ─ Uri List ─ Input is Root with only three dirs", tree, get_last_in_tree(tree)); + free_whole_tree(tree); + + after(); +} + + + +int main() { + before_all(); + + test_getNextInTree_NullIn(); + test_getNextInTree_RootWithoutElements(); + test_getNextInTree_RootWithOnlyDir(); + test_getNextInTree_SingleFolder_RootWithOnlyThreeDirs(); + test_getNextInTree_UriList_RootWithOnlyThreeDirs(); + + after_all(); + return errors == 0 ? 0 : -1; +} diff --git a/tests/test-tree-numberofleaves.c b/tests/test-tree-numberofleaves.c new file mode 100644 index 0000000..dc97b9d --- /dev/null +++ b/tests/test-tree-numberofleaves.c @@ -0,0 +1,219 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#include "utils.h" + + +static void test_getNumberOfLeaves_NullIn() { + before(); + + gint position, total; + get_leaf_position(NULL, &position, &total); + + assert_numbers_equals("#Leaves ─ Null input", -1, position); + assert_numbers_equals("#Leaves ─ Null input", 0, total); + assert_numbers_equals("#Leaves ─ Null input", 0, get_total_number_of_leaves(NULL)); + + after(); +} + +static void test_getNumberOfLeaves_SingleFileNonRecursive_ReturnSameNumberNoMatterWhereInTheTreeWeAre() { + before(); + + int position, total; + GNode *tree = single_file(TRUE, FALSE); + tree = get_root_node(tree); + GNode *node = tree; + +// THIS IS THE STRUCTURE: +// +// test-dir (5 children) +// ├─ .apa.png +// ├─ .depa.gif +// ├─ bepa.png +// ├─ cepa.jpg +// └─ epa.png + + get_leaf_position(node, &position, &total); + assert_numbers_equals("#Leaves ─ Tree root as input ─ position", 0, position); + assert_numbers_equals("#Leaves ─ Tree root as input ─ total", 5, total); + + + node = tree; + assert_numbers_equals("#Leaves ─ Single file, non-recursive ─ Different places in tree", 5, get_total_number_of_leaves(node)); + node = assert_forward_iteration(node, ".apa.png"); + assert_numbers_equals("#Leaves ─ Single file, non-recursive ─ Different places in tree", 5, get_total_number_of_leaves(node)); + node = assert_forward_iteration(node, ".depa.gif"); + assert_numbers_equals("#Leaves ─ Single file, non-recursive ─ Different places in tree", 5, get_total_number_of_leaves(node)); + + free_whole_tree(tree); + after(); +} + +static void test_getNumberOfLeaves_UriListRecursive_ReturnSameNumberNoMatterWhereInTheTreeWeAre() { + before(); + + int position, total; + GNode *tree = uri_list(TRUE, TRUE); + +// THIS IS THE STRUCTURE: +// +// (5 children) +// ├─ .apa.png +// ├─ .depa.gif +// ├─ bepa.png +// ├─ cepa.jpg +// └─┬dir_two (7 children) +// ├─ apa.png +// ├─ bepa.png +// ├─ cepa.png +// ├─┬sub_dir_four (2 children) +// │ ├──subsub (0 children) +// │ └──subsub2 (0 children) +// ├─┬sub_dir_one (3 children) +// │ ├─ img0.png +// │ ├─ img1.png +// │ └─ img2.png +// ├──sub_dir_three (0 children) +// └─┬sub_dir_two (4 children) +// ├─ img0.png +// ├─ img1.png +// ├─ img2.png +// └─ img3.png + + tree = get_root_node(tree); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 0, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + tree = assert_forward_iteration(tree, ".apa.png"); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 1, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + tree = assert_forward_iteration(tree, ".depa.gif"); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 2, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + tree = assert_forward_iteration(tree, "bepa.png"); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 3, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + tree = assert_forward_iteration(tree, "cepa.jpg"); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 4, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + tree = assert_forward_iteration(tree, "apa.png"); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 5, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + tree = assert_forward_iteration(tree, "bepa.png"); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 6, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + tree = assert_forward_iteration(tree, "cepa.png"); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 7, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + tree = assert_forward_iteration(tree, "img0.png"); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 8, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + tree = assert_forward_iteration(tree, "img1.png"); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 9, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + tree = assert_forward_iteration(tree, "img2.png"); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 10, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + tree = assert_forward_iteration(tree, "img0.png"); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 11, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + tree = assert_forward_iteration(tree, "img1.png"); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 12, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + tree = assert_forward_iteration(tree, "img2.png"); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 13, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + tree = assert_forward_iteration(tree, "img3.png"); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 14, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + // Loop from the beginning. + tree = assert_forward_iteration(tree, ".apa.png"); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 1, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + tree = assert_forward_iteration(tree, ".depa.gif"); + get_leaf_position(tree, &position, &total); + assert_numbers_equals("#Leaves ─ Node iteration ─ position", 2, position); + assert_numbers_equals("#Leaves ─ Node iteration ─ total", 14, total); + assert_numbers_equals("#Leaves ─ UriList, recursive ─ Different places in tree", 14, get_total_number_of_leaves(tree)); + + free_whole_tree(tree); + + after(); +} + + + +int main() { + before_all(); + + test_getNumberOfLeaves_NullIn(); + test_getNumberOfLeaves_SingleFileNonRecursive_ReturnSameNumberNoMatterWhereInTheTreeWeAre(); + test_getNumberOfLeaves_UriListRecursive_ReturnSameNumberNoMatterWhereInTheTreeWeAre(); + + after_all(); + return errors == 0 ? 0 : -1; +} diff --git a/tests/test-tree-singlefile.c b/tests/test-tree-singlefile.c new file mode 100644 index 0000000..11af98b --- /dev/null +++ b/tests/test-tree-singlefile.c @@ -0,0 +1,260 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#include "utils.h" + + +static void test_singleFile_NonImageFile() { + before(); + + char *path = append_strings(testdir_path, "/test.txt"); + GError *error = NULL; + + assert_tree_is_null("Non image ─ Include hidden files: F ─ Recursive: F", create_tree_from_single_uri(path, FALSE, FALSE, NULL, NULL, &error)); + assert_error_is_null(error); + g_clear_error(&error); + assert_tree_is_null("Non image ─ Include hidden files: F ─ Recursive: T", create_tree_from_single_uri(path, FALSE, TRUE, NULL, NULL, &error)); + assert_error_is_null(error); + g_clear_error(&error); + assert_tree_is_null("Non image ─ Include hidden files: T ─ Recursive: F", create_tree_from_single_uri(path, TRUE, FALSE, NULL, NULL, &error)); + assert_error_is_null(error); + g_clear_error(&error); + assert_tree_is_null("Non image ─ Include hidden files: T ─ Recursive: T", create_tree_from_single_uri(path, TRUE, TRUE, NULL, NULL, &error)); + assert_error_is_null(error); + g_clear_error(&error); + + free(path); + after(); +} + +static void test_singleFile_NonExistentFile() { + before(); + + char *path = NULL; + GError *error = NULL; + + assert_tree_is_null("Null file ─ Include hidden files: F ─ Recursive: F", create_tree_from_single_uri(path, FALSE, FALSE, NULL, NULL, &error)); + assert_error_is_null(error); + g_clear_error(&error); + assert_tree_is_null("Null file ─ Include hidden files: F ─ Recursive: T", create_tree_from_single_uri(path, FALSE, TRUE, NULL, NULL, &error)); + assert_error_is_null(error); + g_clear_error(&error); + assert_tree_is_null("Null file ─ Include hidden files: T ─ Recursive: F", create_tree_from_single_uri(path, TRUE, FALSE, NULL, NULL, &error)); + assert_error_is_null(error); + g_clear_error(&error); + assert_tree_is_null("Null file ─ Include hidden files: T ─ Recursive: T", create_tree_from_single_uri(path, TRUE, TRUE, NULL, NULL, &error)); + assert_error_is_null(error); + g_clear_error(&error); + + path = "non_existant_file.jpg"; + assert_tree_is_null("Non existant file ─ Include hidden files: F ─ Recursive: F", create_tree_from_single_uri(path, FALSE, FALSE, NULL, NULL, &error)); + assert_error_is_not_null(error); + g_clear_error(&error); + assert_tree_is_null("Non existant file ─ Include hidden files: F ─ Recursive: T", create_tree_from_single_uri(path, FALSE, TRUE, NULL, NULL, &error)); + assert_error_is_not_null(error); + g_clear_error(&error); + assert_tree_is_null("Non existant file ─ Include hidden files: T ─ Recursive: F", create_tree_from_single_uri(path, TRUE, FALSE, NULL, NULL, &error)); + assert_error_is_not_null(error); + g_clear_error(&error); + assert_tree_is_null("Non existant file ─ Include hidden files: T ─ Recursive: T", create_tree_from_single_uri(path, TRUE, TRUE, NULL, NULL, &error)); + assert_error_is_not_null(error); + g_error_free(error); + + after(); +} + +static void test_singleFile_DontIncludeHidden_NotRecursive() { + before(); + + char* expected = KWHT TESTDIRNAME RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + GNode *tree = get_tree(SINGLE_FILE, FALSE, FALSE); + assert_numbers_equals("#Leaves Single file ─ Include hidden files: F ─ Recursive: F", 3, get_total_number_of_leaves(tree)); + + assert_equals("Single file ─ Include hidden files: F ─ Recursive: F", expected, print_and_free_tree(tree)); + + after(); +} + +static void test_singleFile_DontIncludeHidden_Recursive() { + before(); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (1 children)\n\ +│ └─ two.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + GNode *tree = get_tree(SINGLE_FILE, FALSE, TRUE); + assert_numbers_equals("#Leaves Single file ─ Include hidden files: F ─ Recursive: T", 14, get_total_number_of_leaves(tree)); + + assert_equals("Single file ─ Include hidden files: F ─ Recursive: T", expected, print_and_free_tree(tree)); + + after(); +} + +static void test_singleFile_IncludeHidden_NotRecursive() { + before(); + + char* expected = KWHT TESTDIRNAME RESET " (5 children)\n\ +├─ .apa.png\n\ +├─ .depa.gif\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─ epa.png\n\ +"; + + GNode *tree = get_tree(SINGLE_FILE, TRUE, FALSE); + assert_numbers_equals("#Leaves Single file ─ Include hidden files: T ─ Recursive: F", 5, get_total_number_of_leaves(tree)); + + assert_equals("Single file ─ Include hidden files: T ─ Recursive: F", expected, print_and_free_tree(tree)); + + after(); +} + +static void test_singleFile_IncludeHidden_Recursive() { + before(); + + char* expected = KWHT TESTDIRNAME RESET " (7 children)\n\ +├─ .apa.png\n\ +├─ .depa.gif\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ epa.png\n\ +├─┬" KWHT "dir_one" RESET " (3 children)\n\ +│ ├─ .three.png\n\ +│ ├─ two.jpg\n\ +│ └─┬" KWHT ".secrets" RESET " (1 children)\n\ +│ └─ img.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + + GNode *tree = get_tree(SINGLE_FILE, TRUE, TRUE); + assert_numbers_equals("#Leaves Single file ─ Include hidden files: T ─ Recursive: T", 18, get_total_number_of_leaves(tree)); + + assert_equals("Single file ─ Include hidden files: T ─ Recursive: T", expected, print_and_free_tree(tree)); + + after(); +} + + +static void assert_node_has_path(char* path, gboolean include_hidden, gboolean recursive) { + GNode *tree = open_single_file(path, include_hidden, recursive); + assert_equals("Node returned is the one requested ─ Include hidden files: T ─ Recursive: T", path, ((VnrFile*) (tree->data))->path); + free_whole_tree(tree); + free(path); +} + +static void test_singleFile_nodeHasTheRequestedPath() { + before(); + char *path; + + + path = get_absolute_path(testdir_path, "/cepa.jpg"); + assert_node_has_path(path, FALSE, FALSE); + + path = get_absolute_path(testdir_path, "/epa.png"); + assert_node_has_path(path, FALSE, FALSE); + + + path = get_absolute_path(testdir_path, "/cepa.jpg"); + assert_node_has_path(path, FALSE, TRUE); + + path = get_absolute_path(testdir_path, "/epa.png"); + assert_node_has_path(path, FALSE, TRUE); + + + path = get_absolute_path(testdir_path, "/.apa.png"); + assert_node_has_path(path, TRUE, FALSE); + + path = get_absolute_path(testdir_path, "/cepa.jpg"); + assert_node_has_path(path, TRUE, FALSE); + + path = get_absolute_path(testdir_path, "/epa.png"); + assert_node_has_path(path, TRUE, FALSE); + + + path = get_absolute_path(testdir_path, "/.apa.png"); + assert_node_has_path(path, TRUE, TRUE); + + path = get_absolute_path(testdir_path, "/cepa.jpg"); + assert_node_has_path(path, TRUE, TRUE); + + path = get_absolute_path(testdir_path, "/epa.png"); + assert_node_has_path(path, TRUE, TRUE); + + + after(); +} + + + +int main() { + before_all(); + + test_singleFile_NonImageFile(); + test_singleFile_NonExistentFile(); + test_singleFile_DontIncludeHidden_NotRecursive(); + test_singleFile_DontIncludeHidden_Recursive(); + test_singleFile_IncludeHidden_NotRecursive(); + test_singleFile_IncludeHidden_Recursive(); + test_singleFile_nodeHasTheRequestedPath(); + + after_all(); + return errors == 0 ? 0 : -1; +} diff --git a/tests/test-tree-urilist.c b/tests/test-tree-urilist.c new file mode 100644 index 0000000..30aedee --- /dev/null +++ b/tests/test-tree-urilist.c @@ -0,0 +1,286 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#include "utils.h" + + +static void test_uriList_DontIncludeHidden_NotRecursive() { + before(); + + char* expected = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + GNode *tree = get_tree(VALID_LIST, FALSE, FALSE); + assert_numbers_equals("#Leaves Uri List ─ Include hidden files: F ─ Recursive: F", 5, get_total_number_of_leaves(tree)); + + assert_equals("Uri List ─ Include hidden files: F ─ Recursive: F", expected, print_and_free_tree(tree)); + reset_output(); + + + expected = KWHT "" RESET " (4 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ img.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + tree = get_tree(SEMI_INVALID_LIST, FALSE, FALSE); + assert_numbers_equals("#Leaves Uri List ─ Include hidden files: F ─ Recursive: F", 6, get_total_number_of_leaves(tree)); + + assert_equals("Uri List ─ Some invalid files. Include hidden files: F ─ Recursive: F", expected, print_and_free_tree(tree)); + reset_output(); + + + tree = get_tree(COMPLETELY_INVALID_LIST, FALSE, FALSE); + assert_numbers_equals("#Leaves Uri List ─ Include hidden files: F ─ Recursive: F", 0, get_total_number_of_leaves(tree)); + + assert_equals("Uri List ─ Only invalid files. Include hidden files: F ─ Recursive: F", KWHT "" RESET " (0 children)\n", print_and_free_tree(tree)); + + after(); +} + +static void test_uriList_DontIncludeHidden_Recursive() { + before(); + + char* expected = KWHT "" RESET " (3 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + GNode *tree = get_tree(VALID_LIST, FALSE, TRUE); + assert_numbers_equals("#Leaves Uri List ─ Include hidden files: F ─ Recursive: T", 12, get_total_number_of_leaves(tree)); + + assert_equals("Uri List ─ Include hidden files: F ─ Recursive: T", expected, print_and_free_tree(tree)); + reset_output(); + + + expected = KWHT "" RESET " (4 children)\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ img.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + tree = get_tree(SEMI_INVALID_LIST, FALSE, TRUE); + assert_numbers_equals("#Leaves Uri List ─ Include hidden files: F ─ Recursive: T", 13, get_total_number_of_leaves(tree)); + + assert_equals("Uri List ─ Some invalid files. Include hidden files: F ─ Recursive: T", expected, print_and_free_tree(tree)); + reset_output(); + + + tree = get_tree(COMPLETELY_INVALID_LIST, FALSE, TRUE); + assert_numbers_equals("#Leaves Uri List ─ Include hidden files: F ─ Recursive: T", 0, get_total_number_of_leaves(tree)); + + assert_equals("Uri List ─ Only invalid files. Include hidden files: F ─ Recursive: T", KWHT "" RESET " (0 children)\n", print_and_free_tree(tree)); + + after(); +} + +static void test_uriList_IncludeHidden_NotRecursive() { + before(); + + char* expected = KWHT "" RESET " (5 children)\n\ +├─ .apa.png\n\ +├─ .depa.gif\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + GNode *tree = get_tree(VALID_LIST, TRUE, FALSE); + assert_numbers_equals("#Leaves Uri List ─ Include hidden files: T ─ Recursive: F", 7, get_total_number_of_leaves(tree)); + + assert_equals("Uri List ─ Include hidden files: T ─ Recursive: F", expected, print_and_free_tree(tree)); + reset_output(); + + + expected = KWHT "" RESET " (6 children)\n\ +├─ .apa.png\n\ +├─ .depa.gif\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ img.jpg\n\ +└─┬" KWHT "dir_two" RESET " (3 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + └─ cepa.png\n\ +"; + tree = get_tree(SEMI_INVALID_LIST, TRUE, FALSE); + assert_numbers_equals("#Leaves Uri List ─ Include hidden files: T ─ Recursive: F", 8, get_total_number_of_leaves(tree)); + + assert_equals("Uri List ─ Some invalid files. Include hidden files: T ─ Recursive: F", expected, print_and_free_tree(tree)); + reset_output(); + + + tree = get_tree(COMPLETELY_INVALID_LIST, TRUE, FALSE); + assert_numbers_equals("#Leaves Uri List ─ Include hidden files: T ─ Recursive: F", 0, get_total_number_of_leaves(tree)); + + assert_equals("Uri List ─ Only invalid files. Include hidden files: T ─ Recursive: F", KWHT "" RESET " (0 children)\n", print_and_free_tree(tree)); + + after(); +} + +static void test_uriList_IncludeHidden_Recursive() { + before(); + + char* expected = KWHT "" RESET " (5 children)\n\ +├─ .apa.png\n\ +├─ .depa.gif\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + GNode *tree = get_tree(VALID_LIST, TRUE, TRUE); + assert_numbers_equals("#Leaves Uri List ─ Include hidden files: T ─ Recursive: T", 14, get_total_number_of_leaves(tree)); + + assert_equals("Uri List ─ Include hidden files: T ─ Recursive: T", expected, print_and_free_tree(tree)); + reset_output(); + + + + expected = KWHT "" RESET " (6 children)\n\ +├─ .apa.png\n\ +├─ .depa.gif\n\ +├─ bepa.png\n\ +├─ cepa.jpg\n\ +├─ img.jpg\n\ +└─┬" KWHT "dir_two" RESET " (7 children)\n\ + ├─ apa.png\n\ + ├─ bepa.png\n\ + ├─ cepa.png\n\ + ├─┬" KWHT "sub_dir_four" RESET " (2 children)\n\ + │ ├──" KWHT "subsub" RESET " (0 children)\n\ + │ └──" KWHT "subsub2" RESET " (0 children)\n\ + ├─┬" KWHT "sub_dir_one" RESET " (3 children)\n\ + │ ├─ img0.png\n\ + │ ├─ img1.png\n\ + │ └─ img2.png\n\ + ├──" KWHT "sub_dir_three" RESET " (0 children)\n\ + └─┬" KWHT "sub_dir_two" RESET " (4 children)\n\ + ├─ img0.png\n\ + ├─ img1.png\n\ + ├─ img2.png\n\ + └─ img3.png\n\ +"; + tree = get_tree(SEMI_INVALID_LIST, TRUE, TRUE); + assert_numbers_equals("#Leaves Uri List ─ Include hidden files: T ─ Recursive: F", 15, get_total_number_of_leaves(tree)); + + assert_equals("Uri List ─ Some invalid files. Include hidden files: T ─ Recursive: T", expected, print_and_free_tree(tree)); + reset_output(); + + tree = get_tree(COMPLETELY_INVALID_LIST, TRUE, TRUE); + assert_numbers_equals("#Leaves Uri List ─ Include hidden files: T ─ Recursive: F", 0, get_total_number_of_leaves(tree)); + + assert_equals("Uri List ─ Only invalid files. Include hidden files: T ─ Recursive: T", KWHT "" RESET " (0 children)\n", print_and_free_tree(tree)); + + after(); +} + + +static void assert_node_has_path(char* path, gboolean include_hidden, gboolean recursive) { + GNode *tree = get_tree(SEMI_INVALID_LIST, include_hidden, recursive); + assert_equals("Node returned is the first in folder ─ Include hidden files: T ─ Recursive: T", path, ((VnrFile*) (tree->data))->path); + free_whole_tree(tree); +} + +static void test_uriList_nodeHasTheRequestedPath() { + before(); + char *path_normal = get_absolute_path(testdir_path, "/bepa.png"); + char *path_hidden = get_absolute_path(testdir_path, "/.apa.png"); + + assert_node_has_path(path_normal, FALSE, FALSE); + assert_node_has_path(path_normal, FALSE, TRUE); + assert_node_has_path(path_hidden, TRUE, FALSE); + assert_node_has_path(path_hidden, TRUE, TRUE); + + free(path_normal); + free(path_hidden); + after(); +} + + + +int main() { + before_all(); + + test_uriList_DontIncludeHidden_NotRecursive(); + test_uriList_DontIncludeHidden_Recursive(); + test_uriList_IncludeHidden_NotRecursive(); + test_uriList_IncludeHidden_Recursive(); + test_uriList_nodeHasTheRequestedPath(); + + after_all(); + return errors == 0 ? 0 : -1; +} diff --git a/tests/tree-printer.c b/tests/tree-printer.c new file mode 100644 index 0000000..f7bb9e1 --- /dev/null +++ b/tests/tree-printer.c @@ -0,0 +1,98 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#include +#include +#include + +#include "../src/vnr-tree.h" +#include "tree-printer.h" + +char* out; +int output_offset = 0; + +static void print_tree(GNode *tree, char* tree_structure_base); + + +char* create_string(char* base_str, char* append_str) { + char* new_str = malloc(strlen(base_str) + strlen(append_str) + 1); + strcpy(new_str, base_str); + strcat(new_str, append_str); + return new_str; +} + +static char* get_file_name(GNode* node) { + if(node->data != NULL) { + VnrFile* vnrfile = node->data; + return vnrfile->display_name; + } + return ""; +} + +static gboolean is_leaf(GNode *node) { + VnrFile* vnrfile = node->data; + return vnrfile != NULL && !vnrfile->is_directory; // A leaf in the tree can represent an empty directory. Otherwise we could do G_NODE_IS_LEAF(node) +} + +static void print_node(GNode *node, gpointer data) { + if(is_leaf(node)) { + char* append_str = has_more_siblings(node) ? "├─ " : "└─ "; + char* tree_structure = create_string((char*) data, append_str); + + output_offset += snprintf(out + output_offset, (size_t) (OUTPUTSIZE - output_offset), "%s%s\n", tree_structure, get_file_name(node)); + + free(tree_structure); + + } else { + char* tree_structure = create_string((char*) data, ""); + print_tree(node, tree_structure); + free(tree_structure); + } +} + +static void print_tree(GNode *tree, char* tree_structure_base) { + + char* tree_structure_end = ""; + if(!G_NODE_IS_ROOT(tree)) { + if(has_more_siblings(tree)) { + tree_structure_end = has_children(tree) ? "├─┬" : "├──"; + } else { + tree_structure_end = has_children(tree) ? "└─┬" : "└──"; + } + } + + output_offset += snprintf(out + output_offset, (size_t) (OUTPUTSIZE - output_offset), "%s%s" KWHT "%s" RESET " (%i children)\n", + tree_structure_base, tree_structure_end, get_file_name(tree), g_node_n_children(tree)); + + char* append_str = (G_NODE_IS_ROOT(tree) ? "" : (has_more_siblings(tree) ? "│ " : " ")); + char* tree_structure = create_string(tree_structure_base, append_str); + + g_node_children_foreach(tree, + G_TRAVERSE_ALL, + print_node, + tree_structure); + + free(tree_structure); +} + +void pretty_print_tree(GNode *tree, char* output) { + out = output; + output_offset = 0; + print_tree(get_root_node(tree), ""); +} diff --git a/tests/tree-printer.h b/tests/tree-printer.h new file mode 100644 index 0000000..7db9302 --- /dev/null +++ b/tests/tree-printer.h @@ -0,0 +1,32 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#ifndef __TREE_PRINTER_H__ +#define __TREE_PRINTER_H__ + +#include + +#define KWHT "\033[1m\033[37m" +#define RESET "\x1B[0m" +#define OUTPUTSIZE 2048 + +char* create_string(char* base_str, char* append_str); +void pretty_print_tree(GNode *tree, char* out); + +#endif /* __TREE_PRINTER_H__ */ diff --git a/tests/utils.c b/tests/utils.c new file mode 100644 index 0000000..87165e3 --- /dev/null +++ b/tests/utils.c @@ -0,0 +1,554 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#include "utils.h" + +char* output = NULL; +GNode* monitor_test_tree = NULL; + +char *testdir_path; +const int TIMEOUT = 5; +int file_system_changes = 0; +int errors = 0; +gpointer expected_callback_data = (gpointer) 1; + +char* append_strings(char* base_str, char* append_str) { + char* new_str = malloc(strlen(base_str) + strlen(append_str) + 1); + strcpy(new_str, base_str); + strcat(new_str, append_str); + return new_str; +} + +GSList* add_path_to_list(GSList *list, char *parent_dir, char *filename) { + char *path = append_strings(parent_dir, filename); + list = g_slist_prepend(list, path); + return list; +} + +char* get_absolute_path(char *dir, char *filename) { + char *absolute_path; + char *relative_path = append_strings(dir, filename); + + GFile *file = g_file_new_for_path(relative_path); + absolute_path = g_file_get_path(file); + + free(relative_path); + g_object_unref(file); + return absolute_path; +} + + +void file_system_changed_callback(gboolean deleted, char *path, GNode *changed_node, GNode *root, gpointer data) { + file_system_changes++; + + if(!deleted && changed_node != NULL) { + assert_equals("'path' in callback should be equal to that of the changed_node", path, VNR_FILE(changed_node->data)->path); + } + assert_numbers_equals("Callback data should be as expected", (int) (intptr_t) expected_callback_data, + (int) (intptr_t) data); + + if(deleted && changed_node == root) { + // The root was deleted. + monitor_test_tree = NULL; + } + if(monitor_test_tree != NULL) { + monitor_test_tree = get_next_in_tree(root); + } +} + + + +GNode* open_single_file(char* path, gboolean include_hidden, gboolean recursive) { + GNode *tree = NULL; + GError *error = NULL; + + tree = create_tree_from_single_uri(path, include_hidden, recursive, file_system_changed_callback, expected_callback_data, &error); + + assert_error_is_null(error); + free(error); + return tree; +} + +GNode* single_file(gboolean include_hidden, gboolean recursive) { + char *path = append_strings(testdir_path, "/bepa.png"); + GNode *tree = open_single_file(path, include_hidden, recursive); + free(path); + return tree; +} + +GNode* single_folder(gboolean include_hidden, gboolean recursive) { + GNode *tree = NULL; + GError *error = NULL; + char *path = testdir_path; + + tree = create_tree_from_single_uri(path, include_hidden, recursive, file_system_changed_callback, expected_callback_data, &error); + + assert_error_is_null(error); + free(error); + return tree; +} + +GNode* uri_list(gboolean include_hidden, gboolean recursive) { + GNode *tree = NULL; + GError *error = NULL; + GSList *uri_list = NULL; + + uri_list = add_path_to_list(uri_list, testdir_path, "/.apa.png"); + uri_list = add_path_to_list(uri_list, testdir_path, "/bepa.png"); + uri_list = add_path_to_list(uri_list, testdir_path, "/cepa.jpg"); + uri_list = add_path_to_list(uri_list, testdir_path, "/.depa.gif"); + uri_list = add_path_to_list(uri_list, testdir_path, "/test.txt"); + uri_list = add_path_to_list(uri_list, testdir_path, "/dir_two"); + + + tree = create_tree_from_uri_list(uri_list, include_hidden, recursive, file_system_changed_callback, expected_callback_data, &error); + + g_slist_free_full(uri_list, free); + assert_error_is_null(error); + free(error); + return tree; +} + +GNode* uri_list_with_some_invalid_entries(gboolean include_hidden, gboolean recursive) { + GNode *tree = NULL; + GError *error = NULL; + GSList *uri_list = NULL; + + uri_list = add_path_to_list(uri_list, testdir_path, "/.apa.png"); + uri_list = add_path_to_list(uri_list, testdir_path, "/bepa.png"); + uri_list = add_path_to_list(uri_list, testdir_path, "/NON_EXISTANT_FILE.jpg"); + uri_list = add_path_to_list(uri_list, testdir_path, "/cepa.jpg"); + uri_list = add_path_to_list(uri_list, testdir_path, "/dir_one/.secrets/img.jpg"); + uri_list = add_path_to_list(uri_list, testdir_path, "/.depa.gif"); + uri_list = add_path_to_list(uri_list, testdir_path, "/test.txt"); + uri_list = add_path_to_list(uri_list, testdir_path, "/dir_two"); + uri_list = add_path_to_list(uri_list, testdir_path, "/NON_EXISTANT_DIR"); + + tree = create_tree_from_uri_list(uri_list, include_hidden, recursive, file_system_changed_callback, expected_callback_data, &error); + + g_slist_free_full(uri_list, free); + assert_error_is_null(error); + free(error); + return tree; +} + +GNode* uri_list_with_only_invalid_entries(gboolean include_hidden, gboolean recursive) { + GNode *tree = NULL; + GError *error = NULL; + GSList *uri_list = NULL; + + uri_list = add_path_to_list(uri_list, testdir_path, "/NON_EXISTANT_FILE.jpg"); + uri_list = add_path_to_list(uri_list, testdir_path, "/NON_EXISTANT_DIR"); + + + tree = create_tree_from_uri_list(uri_list, include_hidden, recursive, file_system_changed_callback, expected_callback_data, &error); + + g_slist_free_full(uri_list, free); + assert_error_is_null(error); + free(error); + return tree; +} + +GNode* uri_list_with_no_entries(gboolean include_hidden, gboolean recursive) { + GNode *tree = NULL; + GError *error = NULL; + GSList *uri_list = NULL; + + tree = create_tree_from_uri_list(uri_list, include_hidden, recursive, file_system_changed_callback, expected_callback_data, &error); + + assert_error_is_null(error); + free(error); + return tree; +} + +GNode* get_tree(inputtype type, gboolean include_hidden, gboolean recursive) { + + GNode* tree; + if(type == SINGLE_FILE) { + tree = single_file(include_hidden, recursive); + } else if(type == SINGLE_FOLDER) { + tree = single_folder(include_hidden, recursive); + } else if(type == SEMI_INVALID_LIST) { + tree = uri_list_with_some_invalid_entries(include_hidden, recursive); + } else if(type == COMPLETELY_INVALID_LIST) { + tree = uri_list_with_only_invalid_entries(include_hidden, recursive); + } else { + tree = uri_list(include_hidden, recursive); + } + return tree; +} + +char* print_and_free_tree(GNode *tree) { + pretty_print_tree(tree, output); + + free_whole_tree(tree); + return output; +} + + + +/////////////////////////////// + + +void create_dir(char *parent_dir, char *dir_name) { + char *path = append_strings(parent_dir, dir_name); + struct stat st = {0}; + + if(stat(path, &st) == -1) { + mkdir(path, 0700); + } + free(path); +} + +void create_file(char *parent_dir, char *filename) { + char *path = append_strings(parent_dir, filename); + + char *dot = strrchr(path, '.'); + if(dot && (!strcmp(dot, ".jpg") || !strcmp(dot, ".png") || !strcmp(dot, ".gif"))) { + // Write binary image data. + + size_t size = 73; + int img[size]; + int i = 0; + img[i++] = 0x89; img[i++] = 0x50; img[i++] = 0x4e; img[i++] = 0x47; img[i++] = 0x0d; img[i++] = 0x0a; + img[i++] = 0x1a; img[i++] = 0x0a; img[i++] = 0x00; img[i++] = 0x00; img[i++] = 0x00; img[i++] = 0x0d; + img[i++] = 0x49; img[i++] = 0x48; img[i++] = 0x44; img[i++] = 0x52; img[i++] = 0x00; img[i++] = 0x00; + img[i++] = 0x00; img[i++] = 0x01; img[i++] = 0x00; img[i++] = 0x00; img[i++] = 0x00; img[i++] = 0x01; + img[i++] = 0x01; img[i++] = 0x00; img[i++] = 0x00; img[i++] = 0x00; img[i++] = 0x00; img[i++] = 0x37; + img[i++] = 0x6e; img[i++] = 0xf9; img[i++] = 0x24; img[i++] = 0x00; img[i++] = 0x00; img[i++] = 0x00; + img[i++] = 0x10; img[i++] = 0x49; img[i++] = 0x44; img[i++] = 0x41; img[i++] = 0x54; img[i++] = 0x78; + img[i++] = 0x9c; img[i++] = 0x62; img[i++] = 0x60; img[i++] = 0x01; img[i++] = 0x00; img[i++] = 0x00; + img[i++] = 0x00; img[i++] = 0xff; img[i++] = 0xff; img[i++] = 0x03; img[i++] = 0x00; img[i++] = 0x00; + img[i++] = 0x06; img[i++] = 0x00; img[i++] = 0x05; img[i++] = 0x57; img[i++] = 0xbf; img[i++] = 0xab; + img[i++] = 0xd4; img[i++] = 0x00; img[i++] = 0x00; img[i++] = 0x00; img[i++] = 0x00; img[i++] = 0x49; + img[i++] = 0x45; img[i++] = 0x4e; img[i++] = 0x44; img[i++] = 0xae; img[i++] = 0x42; img[i++] = 0x60; + img[i] = 0x82; + + FILE *fh = fopen(path, "wb"); + if(fh != NULL) { + fwrite(&img, sizeof(img), 1, fh); + fclose(fh); + } + + } else { + // Create empty file + fclose(fopen(path, "w")); + } + free(path); +} + +void create_file_structure() { + + create_dir (testdir_path, ""); + create_file(testdir_path, "/test.txt"); + create_file(testdir_path, "/cepa.jpg"); + create_file(testdir_path, "/.apa.png"); + create_file(testdir_path, "/not_an_image.yo"); + create_file(testdir_path, "/.depa.gif"); + create_file(testdir_path, "/bepa.png"); + create_file(testdir_path, "/epa.png"); + + create_dir (testdir_path, "/dir_one"); + create_file(testdir_path, "/dir_one/one.txt"); + create_file(testdir_path, "/dir_one/two.jpg"); + create_file(testdir_path, "/dir_one/.three.png"); + + create_dir (testdir_path, "/dir_one/.secrets"); + create_file(testdir_path, "/dir_one/.secrets/img.jpg"); + + create_dir (testdir_path, "/dir_two"); + create_file(testdir_path, "/dir_two/bepa.png"); + + create_dir (testdir_path, "/dir_two/sub_dir_two"); + create_file(testdir_path, "/dir_two/sub_dir_two/img0.png"); + create_file(testdir_path, "/dir_two/sub_dir_two/img2.png"); + create_file(testdir_path, "/dir_two/sub_dir_two/img1.png"); + create_file(testdir_path, "/dir_two/sub_dir_two/img3.png"); + + create_dir (testdir_path, "/dir_two/sub_dir_one"); + create_file(testdir_path, "/dir_two/sub_dir_one/img0.png"); + create_file(testdir_path, "/dir_two/sub_dir_one/img2.png"); + create_file(testdir_path, "/dir_two/sub_dir_one/img1.png"); + + create_file(testdir_path, "/dir_two/apa.png"); + create_file(testdir_path, "/dir_two/cepa.png"); + + create_dir (testdir_path, "/dir_two/sub_dir_three"); + create_dir (testdir_path, "/dir_two/sub_dir_four"); + create_dir (testdir_path, "/dir_two/sub_dir_four/subsub"); + create_dir (testdir_path, "/dir_two/sub_dir_four/subsub2"); +} + +void remove_file(char *dir, char *filename) { + char *path = append_strings(dir, filename); + unlink(path); + free(path); +} + +int remove_directory(char *parent_dir, char *sub_dir) { + char *path = append_strings(parent_dir, sub_dir); + + DIR *d = opendir(path); + size_t path_len = strlen(path); + int r = -1; + + if(d) { + struct dirent *p; + r = 0; + + while(!r && (p = readdir(d))) { + int r2 = -1; + char *buf; + size_t len; + + /* Skip the names "." and ".." as we don't want to recurse on them. */ + if(!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) { + continue; + } + + len = path_len + strlen(p->d_name) + 2; + buf = malloc(len); + + if(buf) { + struct stat statbuf; + + snprintf(buf, len, "%s/%s", path, p->d_name); + + if(!stat(buf, &statbuf)) { + if(S_ISDIR(statbuf.st_mode)) { + r2 = remove_directory(buf, ""); + } else { + r2 = unlink(buf); + } + } + + free(buf); + } + r = r2; + } + closedir(d); + } + + if(!r) { + r = rmdir(path); + } + + free(path); + return r; +} + +void remove_test_directory() { + remove_directory(testdir_path, ""); +} + +static void create_temp_dir() { + create_dir(TESTDIRBASE, TESTDIRNAME); + char template[] = TESTDIRBASE TESTDIRNAME "/XXXXXX"; + char *dir_name = mkdtemp(template); + testdir_path = append_strings(dir_name, "/" TESTDIRNAME); +} + +static void remove_temp_dir() { + char parent[strlen(testdir_path)]; + const char *last_slash = strrchr(testdir_path, '/'); + if(last_slash) { + strncpy(parent, testdir_path, last_slash - testdir_path); + parent[last_slash - testdir_path] = '\0'; + + remove_directory(parent, ""); + } + free(testdir_path); +} + + + +/////////////////////////////// + + + +void before_all() { + create_temp_dir(); +} + +void after_all() { + remove_temp_dir(); +} + +void before() { + output = (char*) malloc(sizeof(char) * OUTPUTSIZE); + file_system_changes = 0; + create_file_structure(); +} + +void after() { + if(output != NULL) { + free(output); + } + output = NULL; + if(monitor_test_tree != NULL) { + free_whole_tree(monitor_test_tree); + monitor_test_tree = NULL; + } + remove_test_directory(); +} + +void reset_output() { + if(output != NULL) { + free(output); + } + output = (char*) malloc(sizeof(char) * OUTPUTSIZE); +} + +void assert_equals(char* description, char* expected, char* actual) { + if(strcmp(expected, actual) == 0) { + printf(KGRN "[PASS] %s\n" RESET, description); + } else { + printf("\n"); + printf(KRED "[FAIL] %s\n" RESET, description); + printf("Expected:\n'%s'\n\nActual:\n'%s'\n\n", expected, actual); + fprintf(stderr, "* %s\n", description); + errors++; + } +} + +void assert_numbers_equals(char* description, int expected, int actual) { + if(expected == actual) { + printf(KGRN "[PASS] %s\n" RESET, description); + } else { + printf("\n"); + printf(KRED "[FAIL] %s\n" RESET, description); + printf("Expected: %i, Actual: %i\n\n", expected, actual); + fprintf(stderr, "* %s\n", description); + errors++; + } +} + +void assert_trees_equal(char* description, GNode* expected, GNode* actual) { + char *expectedoutput = (char*) malloc(sizeof(char) * OUTPUTSIZE); + char *actualoutput = (char*) malloc(sizeof(char) * OUTPUTSIZE); + + pretty_print_tree(expected, expectedoutput); + pretty_print_tree(actual, actualoutput); + + assert_equals(description, expectedoutput, actualoutput); + + free(expectedoutput); + free(actualoutput); +} + +void assert_tree_is_null(char* description, GNode* tree) { + if(tree == NULL) { + printf(KGRN "[PASS] %s\n" RESET, description); + } else { + printf("\n"); + printf(KRED "[FAIL] %s\n" RESET, description); + fprintf(stderr, "* %s\n", description); + errors++; + } +} + +void assert_error_is_null(GError* error) { + if(error != NULL) { + printf(KRED "[FAIL] Error should not be set! Was: %s\n" RESET, error->message); + fprintf(stderr, "* Error should not be set! Was: %s\n", error->message); + errors++; + } +} + +void assert_error_is_not_null(GError* error) { + if(error == NULL) { + printf(KRED "[FAIL] Error should be set!\n" RESET); + fprintf(stderr, "* Error should be set!\n"); + errors++; + } +} + +// Due to the non-deterministic way that file monitors are triggered, there could be several triggers for +// one action, if e.g. a directory and its root both have filemonitors, and the directory is deleted. +// Test the minimum amount of expected file system changes, and let the underlying file system raise as +// many triggers as it needs. +void assert_file_system_changes_at_least(int expected) { + if(file_system_changes < expected) { + printf("\n"); + printf(KRED "[FAIL] %s\n" RESET, "Mismatch in number of file system changes"); + printf("Expected: %i, Actual: %i\n\n", expected, file_system_changes); + errors++; + } +} + +void wait_until_tree_is_null() { + clock_t start = clock(); + while((clock() - start) / CLOCKS_PER_SEC < TIMEOUT) { + + g_main_context_iteration(NULL, FALSE); + + if(monitor_test_tree == NULL) { + break; + } + } +} + +void wait_until_file_system_changes_is_as_expected(int expected) { + clock_t start = clock(); + while((clock() - start) / CLOCKS_PER_SEC < TIMEOUT) { + + g_main_context_iteration(NULL, FALSE); + + if(file_system_changes == expected) { + break; + } + } +} + +void wait_until_tree_is_as_expected(GNode* tree, char* expected) { + clock_t start = clock(); + while((clock() - start) / CLOCKS_PER_SEC < TIMEOUT) { + + reset_output(); + pretty_print_tree(tree, output); + g_main_context_iteration(NULL, FALSE); + + if(strcmp(expected, output) == 0) { + break; + } + } +} + + + +static void assert_iteration(char* description_base, GNode* node, char* expected_file_name) { + VnrFile* vnrfile = node->data; + + char* description = create_string(description_base, expected_file_name); + assert_equals(description, expected_file_name, vnrfile->display_name); + + free(description); +} + +GNode* assert_forward_iteration(GNode* node, char* expected_file_name) { + GNode *next = get_next_in_tree(node); + assert_iteration("Get Next ─ Iterating ─ ", next, expected_file_name); + return next; +} + +GNode* assert_backward_iteration(GNode* node, char* expected_file_name) { + GNode *prev = get_prev_in_tree(node); + assert_iteration("Get Prev ─ Iterating ─ ", prev, expected_file_name); + return prev; +} diff --git a/tests/utils.h b/tests/utils.h new file mode 100644 index 0000000..25524aa --- /dev/null +++ b/tests/utils.h @@ -0,0 +1,91 @@ +/* + * Copyright © 2009-2018 Siyan Panayotov + * + * This file is part of Viewnior. + * + * Viewnior is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Viewnior 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Viewnior. If not, see . + */ + +#ifndef __UTILS_H__ +#define __UTILS_H__ + +#include +#include +#include + +#include +#include +#include + +#include "../src/vnr-tree.h" +#include "../src/vnr-file.h" +#include "tree-printer.h" + +#define TESTDIRNAME "tests" +#define TESTDIRBASE "./" + +#define KRED "\x1B[31m" +#define KGRN "\x1B[32m" +#define KWHT "\033[1m\033[37m" +#define RESET "\x1B[0m" +#define OUTPUTSIZE 2048 + +extern char* testdir_path; +extern int errors; +extern char* output; +extern GNode* monitor_test_tree; + +typedef enum {SINGLE_FILE, SINGLE_FOLDER, VALID_LIST, SEMI_INVALID_LIST, COMPLETELY_INVALID_LIST} inputtype; + +char* append_strings(char* base_str, char* append_str); +GSList* add_path_to_list(GSList *list, char *parent_dir, char *filename); +char* get_absolute_path(char *dir, char *filename); + +GNode* open_single_file(char* path, gboolean include_hidden, gboolean recursive); +GNode* single_file(gboolean include_hidden, gboolean recursive); +GNode* single_folder(gboolean include_hidden, gboolean recursive); +GNode* uri_list(gboolean include_hidden, gboolean recursive); +GNode* uri_list_with_some_invalid_entries(gboolean include_hidden, gboolean recursive); +GNode* uri_list_with_only_invalid_entries(gboolean include_hidden, gboolean recursive); +GNode* get_tree(inputtype type, gboolean include_hidden, gboolean recursive); +char* print_and_free_tree(GNode *tree); +GNode* uri_list_with_no_entries(gboolean include_hidden, gboolean recursive); + +void create_dir(char *parent_dir, char *dir_name); +void create_file(char *parent_dir, char *filename); +void create_file_structure(); +void remove_file(char* dir, char *filename); +int remove_directory(char *parent_dir, char *path); +void remove_test_directory(); + +void before_all(); +void after_all(); +void before(); +void after(); +void reset_output(); +void assert_equals(char* description, char* expected, char* actual); +void assert_numbers_equals(char* description, int expected, int actual); +void assert_trees_equal(char* description, GNode* expected, GNode* actual); +void assert_tree_is_null(char* description, GNode* tree); +void assert_error_is_null(GError* error); +void assert_error_is_not_null(GError* error); +void assert_file_system_changes_at_least(int expected); +void wait_until_tree_is_null(); +void wait_until_tree_is_as_expected(GNode* tree, char* expected); +void wait_until_file_system_changes_is_as_expected(int expected); + +GNode* assert_forward_iteration(GNode* node, char* expected_file_name); +GNode* assert_backward_iteration(GNode* node, char* expected_file_name); + +#endif /* __UTILS_H__ */