/* GTK - The GIMP Toolkit * autotestfilesystem.c: Automated tests for GtkFileSystem implementations * Copyright (C) 2005, Novell, Inc. * * Authors: * Federico Mena-Quintero * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #define GTK_FILE_SYSTEM_ENABLE_UNSUPPORTED #include "config.h" #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #ifdef G_OS_WIN32 #include #define rmdir(d) _rmdir(d) #endif #define CALLBACK_TIMEOUT_MS 3000 /* Period after which the callback must have been called */ #define CANCEL_TIMEOUT_MS 100 /* We'll sleep for this much time before cancelling */ #define GET_FOLDER_FILENAME "/etc" #define GET_INFO_FILENAME "/etc/passwd" #define CREATE_FOLDER_FILENAME "/tmp/autotestfilesystem-tmp" #define VOLUME_MOUNT_FILENAME "/" /* This is stolen from gtkfilechooserdefault.c:set_file_system_backend() */ static GtkFileSystem * get_file_system (void) { GtkFileSystem *file_system = NULL; #if 1 file_system = gtk_file_system_create ("gnome-vfs"); #else GtkSettings *settings = gtk_settings_get_default (); gchar *default_backend = NULL; g_object_get (settings, "gtk-file-chooser-backend", &default_backend, NULL); if (default_backend) { file_system = gtk_file_system_create (default_backend); g_free (default_backend); } if (!file_system) { #if defined (G_OS_UNIX) file_system = gtk_file_system_unix_new (); #elif defined (G_OS_WIN32) file_system = gtk_file_system_win32_new (); #else #error "No default filesystem implementation on the platform" #endif } #endif return file_system; } /***** Testing infrastructure *****/ typedef struct { gboolean callback_was_called; gboolean timeout_was_called; } TestCallbackClosure; static void notify_callback_called (TestCallbackClosure *closure) { closure->callback_was_called = TRUE; gtk_main_quit (); } static gboolean timeout_cb (gpointer data) { TestCallbackClosure *closure; closure = data; closure->timeout_was_called = TRUE; gtk_main_quit (); return FALSE; } static void wait_for_callback (TestCallbackClosure *closure) { gdk_threads_add_timeout (CALLBACK_TIMEOUT_MS, timeout_cb, closure); gtk_main (); } typedef struct { const char *test_name; gboolean callback_must_be_called; gpointer (* setup_fn) (GtkFileSystem *file_system, gboolean *success, char **failure_reason, TestCallbackClosure *callback_closure); void (* cleanup_fn) (gpointer data, gboolean *success, char **failure_reason); } TestSpec; static gboolean run_test (TestSpec *test_spec) { GtkFileSystem *file_system; TestCallbackClosure closure; gboolean success_setup, success_cleanup; gpointer test_data; gboolean callback_success; gboolean passed; char *setup_failure_reason; char *cleanup_failure_reason; file_system = get_file_system (); if (!file_system) { printf ("FAIL: test \"%s\"\n", test_spec->test_name); printf (" could not create file system!\n"); return FALSE; } closure.callback_was_called = FALSE; closure.timeout_was_called = FALSE; success_setup = success_cleanup = callback_success = FALSE; setup_failure_reason = cleanup_failure_reason = NULL; test_data = test_spec->setup_fn (file_system, &success_setup, &setup_failure_reason, &closure); if (success_setup) { wait_for_callback (&closure); test_spec->cleanup_fn (test_data, &success_cleanup, &cleanup_failure_reason); callback_success = (test_spec->callback_must_be_called == closure.callback_was_called); } g_object_unref (file_system); passed = (success_setup && success_cleanup && callback_success); printf ("%s: test \"%s\"\n", passed ? "PASS" : "FAIL", test_spec->test_name); if (!passed) { if (!success_setup) printf (" failure during setup: %s\n", setup_failure_reason ? setup_failure_reason : "unknown failure"); else { if (!success_cleanup) printf (" failure during cleanup: %s\n", cleanup_failure_reason ? cleanup_failure_reason : "unknown failure"); if (!callback_success) printf (" callback %s called but it %s called\n", test_spec->callback_must_be_called ? "MUST BE" : "MUST NOT BE", closure.callback_was_called ? "WAS" : "WAS NOT"); } } g_free (setup_failure_reason); g_free (cleanup_failure_reason); return passed; } static gboolean run_tests (TestSpec *test_specs, int num_tests) { int i; int num_passed; num_passed = 0; for (i = 0; i < num_tests; i++) if (run_test (test_specs + i)) num_passed++; if (num_passed == num_tests) printf ("ALL TESTS PASSED\n"); else printf ("%d of %d tests FAILED\n", (num_tests - num_passed), num_tests); return (num_passed == num_tests); } /***** Test functions *****/ static void sleep_and_cancel_handle (GtkFileSystemHandle *handle) { g_usleep (CANCEL_TIMEOUT_MS * 1000); gtk_file_system_cancel_operation (handle); } /* get_folder */ struct get_folder_data { TestCallbackClosure *callback_closure; GtkFileSystemHandle *handle; GtkFileFolder *folder; }; static void get_folder_cb (GtkFileSystemHandle *handle, GtkFileFolder *folder, const GError *error, gpointer data) { struct get_folder_data *get_folder_data; get_folder_data = data; get_folder_data->folder = folder; notify_callback_called (get_folder_data->callback_closure); } static gpointer get_folder_generic_setup (GtkFileSystem *file_system, gboolean *success, char **failure_reason, TestCallbackClosure *callback_closure) { struct get_folder_data *get_folder_data; GtkFilePath *path; path = gtk_file_system_filename_to_path (file_system, GET_FOLDER_FILENAME); if (!path) { *success = FALSE; *failure_reason = g_strdup_printf ("could not turn \"%s\" into a GtkFilePath", GET_FOLDER_FILENAME); return NULL; } get_folder_data = g_new (struct get_folder_data, 1); get_folder_data->callback_closure = callback_closure; get_folder_data->folder = NULL; get_folder_data->handle = gtk_file_system_get_folder (file_system, path, GTK_FILE_INFO_ALL, get_folder_cb, get_folder_data); gtk_file_path_free (path); if (!get_folder_data->handle) { g_free (get_folder_data); *success = FALSE; *failure_reason = g_strdup ("gtk_file_system_get_folder() returned a NULL handle"); return NULL; } *success = TRUE; return get_folder_data; } static gpointer get_folder_no_cancel_setup (GtkFileSystem *file_system, gboolean *success, char **failure_reason, TestCallbackClosure *callback_closure) { return get_folder_generic_setup (file_system, success, failure_reason, callback_closure); } static void get_folder_cleanup (gpointer data, gboolean *success, char **failure_reason) { struct get_folder_data *get_folder_data; get_folder_data = data; if (get_folder_data->folder) g_object_unref (get_folder_data->folder); g_free (get_folder_data); *success = TRUE; } static gpointer get_folder_with_cancel_setup (GtkFileSystem *file_system, gboolean *success, char **failure_reason, TestCallbackClosure *callback_closure) { struct get_folder_data *get_folder_data; get_folder_data = get_folder_generic_setup (file_system, success, failure_reason, callback_closure); if (*success) sleep_and_cancel_handle (get_folder_data->handle); return get_folder_data; } /* get_info */ struct get_info_data { TestCallbackClosure *callback_closure; GtkFileSystemHandle *handle; }; static void get_info_cb (GtkFileSystemHandle *handle, const GtkFileInfo *file_info, const GError *error, gpointer data) { struct get_info_data *get_info_data; get_info_data = data; notify_callback_called (get_info_data->callback_closure); } static gpointer get_info_generic_setup (GtkFileSystem *file_system, gboolean *success, char **failure_reason, TestCallbackClosure *callback_closure) { GtkFilePath *path; struct get_info_data *get_info_data; path = gtk_file_system_filename_to_path (file_system, GET_INFO_FILENAME); if (!path) { *success = FALSE; *failure_reason = g_strdup_printf ("could not turn \"%s\" into a GtkFilePath", GET_INFO_FILENAME); return NULL; } get_info_data = g_new (struct get_info_data, 1); get_info_data->callback_closure = callback_closure; get_info_data->handle = gtk_file_system_get_info (file_system, path, GTK_FILE_INFO_ALL, get_info_cb, get_info_data); gtk_file_path_free (path); if (!get_info_data->handle) { g_free (get_info_data); *success = FALSE; *failure_reason = g_strdup ("gtk_file_system_get_info() returned a NULL handle"); return NULL; } *success = TRUE; return get_info_data; } static gpointer get_info_no_cancel_setup (GtkFileSystem *file_system, gboolean *success, char **failure_reason, TestCallbackClosure *callback_closure) { return get_info_generic_setup (file_system, success, failure_reason, callback_closure); } static void get_info_cleanup (gpointer data, gboolean *success, char **failure_reason) { struct get_info_data *get_info_data; get_info_data = data; g_free (get_info_data); *success = TRUE; } static gpointer get_info_with_cancel_setup (GtkFileSystem *file_system, gboolean *success, char **failure_reason, TestCallbackClosure *callback_closure) { struct get_info_data *get_info_data; get_info_data = get_info_generic_setup (file_system, success, failure_reason, callback_closure); if (*success) sleep_and_cancel_handle (get_info_data->handle); return get_info_data; } /* create_folder */ struct create_folder_data { TestCallbackClosure *callback_closure; GtkFileSystemHandle *handle; }; static void create_folder_cb (GtkFileSystemHandle *handle, const GtkFilePath *path, const GError *error, gpointer data) { struct get_folder_data *get_folder_data; get_folder_data = data; notify_callback_called (get_folder_data->callback_closure); } static gpointer create_folder_generic_setup (GtkFileSystem *file_system, gboolean *success, char **failure_reason, TestCallbackClosure *callback_closure) { GtkFilePath *path; struct create_folder_data *create_folder_data; path = gtk_file_system_filename_to_path (file_system, CREATE_FOLDER_FILENAME); if (!path) { *success = FALSE; *failure_reason = g_strdup_printf ("could not turn \"%s\" into a GtkFilePath", CREATE_FOLDER_FILENAME); return NULL; } create_folder_data = g_new (struct create_folder_data, 1); create_folder_data->callback_closure = callback_closure; create_folder_data->handle = gtk_file_system_create_folder (file_system, path, create_folder_cb, create_folder_data); gtk_file_path_free (path); if (!create_folder_data->handle) { g_free (create_folder_data); *success = FALSE; *failure_reason = g_strdup ("gtk_file_system_create_folder() returned a NULL handle"); return NULL; } *success = TRUE; return create_folder_data; } static gpointer create_folder_no_cancel_setup (GtkFileSystem *file_system, gboolean *success, char **failure_reason, TestCallbackClosure *callback_closure) { return create_folder_generic_setup (file_system, success, failure_reason, callback_closure); } static void create_folder_cleanup (gpointer data, gboolean *success, char **failure_reason) { struct create_folder_data *create_folder_data; create_folder_data = data; rmdir (CREATE_FOLDER_FILENAME); g_free (create_folder_data); *success = TRUE; } static gpointer create_folder_with_cancel_setup (GtkFileSystem *file_system, gboolean *success, char **failure_reason, TestCallbackClosure *callback_closure) { struct create_folder_data *create_folder_data; create_folder_data = create_folder_generic_setup (file_system, success, failure_reason, callback_closure); if (*success) sleep_and_cancel_handle (create_folder_data->handle); return create_folder_data; } /* volume_mount */ struct volume_mount_data { TestCallbackClosure *callback_closure; GtkFileSystemVolume *volume; GtkFileSystemHandle *handle; }; static void volume_mount_cb (GtkFileSystemHandle *handle, GtkFileSystemVolume *volume, const GError *error, gpointer data) { struct volume_mount_data *volume_mount_data; volume_mount_data = data; notify_callback_called (volume_mount_data->callback_closure); } static gpointer volume_mount_generic_setup (GtkFileSystem *file_system, gboolean *success, char **failure_reason, TestCallbackClosure *callback_closure) { GtkFilePath *path; struct volume_mount_data *volume_mount_data; path = gtk_file_system_filename_to_path (file_system, VOLUME_MOUNT_FILENAME); if (!path) { *success = FALSE; *failure_reason = g_strdup_printf ("could not turn \"%s\" into a GtkFilePath", VOLUME_MOUNT_FILENAME); return NULL; } volume_mount_data = g_new (struct volume_mount_data, 1); volume_mount_data->callback_closure = callback_closure; volume_mount_data->volume = gtk_file_system_get_volume_for_path (file_system, path); gtk_file_path_free (path); if (!volume_mount_data->volume) { g_free (volume_mount_data); *success = FALSE; *failure_reason = g_strdup ("gtk_file_system_get_volume_for_path() returned a NULL volume"); return NULL; } volume_mount_data->handle = gtk_file_system_volume_mount (file_system, volume_mount_data->volume, volume_mount_cb, volume_mount_data); if (!volume_mount_data->handle) { g_object_unref (volume_mount_data->volume); g_free (volume_mount_data); *success = FALSE; *failure_reason = g_strdup ("gtk_file_system_volume_mount() returned a NULL handle"); return NULL; } *success = TRUE; return volume_mount_data; } static gpointer volume_mount_no_cancel_setup (GtkFileSystem *file_system, gboolean *success, char **failure_reason, TestCallbackClosure *callback_closure) { return volume_mount_generic_setup (file_system, success, failure_reason, callback_closure); } static void volume_mount_cleanup (gpointer data, gboolean *success, char **failure_reason) { struct volume_mount_data *volume_mount_data; volume_mount_data = data; g_object_unref (volume_mount_data->volume); g_free (volume_mount_data); *success = TRUE; } static gpointer volume_mount_with_cancel_setup (GtkFileSystem *file_system, gboolean *success, char **failure_reason, TestCallbackClosure *callback_closure) { struct volume_mount_data *volume_mount_data; volume_mount_data = volume_mount_generic_setup (file_system, success, failure_reason, callback_closure); if (*success) sleep_and_cancel_handle (volume_mount_data->handle); return volume_mount_data; } /* tests */ static TestSpec tests[] = { { "get_folder no cancel", TRUE, get_folder_no_cancel_setup, get_folder_cleanup }, { "get_folder with cancel", FALSE, get_folder_with_cancel_setup, get_folder_cleanup }, { "get_info no cancel", TRUE, get_info_no_cancel_setup, get_info_cleanup }, { "get_info with cancel", FALSE, get_info_with_cancel_setup, get_info_cleanup }, { "create_folder no cancel", TRUE, create_folder_no_cancel_setup, create_folder_cleanup }, { "create_folder with cancel", FALSE, create_folder_with_cancel_setup, create_folder_cleanup }, { "volume_mount no cancel", TRUE, volume_mount_no_cancel_setup, volume_mount_cleanup }, { "volume_mount with cancel", FALSE, volume_mount_with_cancel_setup, volume_mount_cleanup } }; /***** main *****/ static GLogFunc default_log_handler; static int num_warnings; static int num_errors; static int num_critical_errors; static void log_override_cb (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { if (log_level & G_LOG_LEVEL_WARNING) num_warnings++; if (log_level & G_LOG_LEVEL_ERROR) num_errors++; if (log_level & G_LOG_LEVEL_CRITICAL) num_critical_errors++; (* default_log_handler) (log_domain, log_level, message, user_data); } static void log_test (gboolean passed, const char *test_name, ...) { va_list args; char *str; va_start (args, test_name); str = g_strdup_vprintf (test_name, args); va_end (args); g_printf ("%s: %s\n", passed ? "PASSED" : "FAILED", str); g_free (str); } int main (int argc, char **argv) { gboolean passed; gboolean zero_warnings; gboolean zero_errors; gboolean zero_critical_errors; default_log_handler = g_log_set_default_handler (log_override_cb, NULL); gtk_init (&argc, &argv); /* Start tests */ passed = run_tests (tests, G_N_ELEMENTS (tests)); /* Warnings and errors */ zero_warnings = num_warnings == 0; zero_errors = num_errors == 0; zero_critical_errors = num_critical_errors == 0; log_test (zero_warnings, "main(): zero warnings (actual number %d)", num_warnings); log_test (zero_errors, "main(): zero errors (actual number %d)", num_errors); log_test (zero_critical_errors, "main(): zero critical errors (actual number %d)", num_critical_errors); /* Done */ passed = passed && zero_warnings && zero_errors && zero_critical_errors; log_test (passed, "main(): ALL TESTS"); return 0; }