From d958afd9fd7078a6eaee17df0a0b1e71504fc3b1 Mon Sep 17 00:00:00 2001 From: pierre lamot Date: Thu, 26 Feb 2015 14:37:45 +0100 Subject: [PATCH] eio: add OSX implementation of eio_monitor based on FSEvent this patch adds an implementation of eio_monitor based on FSEvent for OSX. This implentation has some limitations compared to inotify implementation. Folowing events are not detected: - EIO_MONITOR_FILE_CLOSED - EIO_MONITOR_SELF_RENAME - EIO_MONITOR_SELF_DELETED It should be noted that some events that happend before the call to eio_monitor_add can be catched. This is why sleep timers have been added in the test suite. Tests have been added to check uncovered scenarios. some things might still be improved: - self_deleted events for files might be handled by checking the file_name manually - self_deleted events for directories might be handled by setting kFSEventStreamCreateFlagWatchRoot. I've noticed by doing so that a lot more unwanted event are raised Signed-off-by: Cedric BAIL --- configure.ac | 67 +++-- src/Makefile_Eio.am | 7 + src/lib/eio/Eio.h | 8 +- src/lib/eio/eio_monitor_cocoa.c | 405 +++++++++++++++++++++++++++++++ src/lib/eio/eio_monitor_poll.c | 2 +- src/tests/eio/eio_test_monitor.c | 404 +++++++++++++++++++++++------- 6 files changed, 784 insertions(+), 109 deletions(-) create mode 100644 src/lib/eio/eio_monitor_cocoa.c diff --git a/configure.ac b/configure.ac index 82da49dc8d..2996474d90 100644 --- a/configure.ac +++ b/configure.ac @@ -417,19 +417,8 @@ sys/mman.h \ netinet/in.h \ ]) -have_inotify="${ac_cv_header_sys_inotify_h}" -AM_CONDITIONAL([HAVE_INOTIFY], [test "x${have_inotify}" = "xyes"]) - -have_notify_win32="${have_win32}" -AC_DEFINE_IF([HAVE_NOTIFY_WIN32], - [test "x${have_notify_win32}" = "xyes"], [1], - [File monitoring with Windows notification]) -AM_CONDITIONAL([HAVE_NOTIFY_WIN32], [test "x${have_notify_win32}" = "xyes"]) - - EFL_CHECK_PATH_MAX - #### Checks for types # wchar_t @@ -1329,11 +1318,11 @@ AC_ARG_ENABLE([cocoa], ], [want_cocoa="no"]) - if test "${want_cocoa}" = "yes"; then - #test cocoa requirements (objc and Cocoa/Cocoa.h) - cocoa_ldflags="" - have_cocoa="no" - m4_ifdef([AC_PROG_OBJC], +if test "${want_cocoa}" = "yes"; then + #test cocoa requirements (objc and Cocoa/Cocoa.h) + cocoa_ldflags="" + have_cocoa="no" + m4_ifdef([AC_PROG_OBJC], [ if test "x${have_gnu_objc}" = "xyes" ; then AC_LANG_PUSH([Objective C]) @@ -1368,6 +1357,38 @@ window = [[NSWindow alloc] fi AC_SUBST(cocoa_ldflags) +if test "x${want_cocoa}" = "xyes"; then + #test cocoa requirements (objc and Cocoa/Cocoa.h) + cocoa_coreservices_ldflags="" + have_cocoa_coreservices="no" + m4_ifdef([AC_PROG_OBJC], + [ + if test "x${have_gnu_objc}" = "xyes" ; then + AC_LANG_PUSH([Objective C]) + LIBS_save="$LIBS" + LIBS="$LIBS -framework CoreServices" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include + ]], + [[ +//test function here + ]])], + [ + have_cocoa_coreservices="yes" + cocoa_coreservices_ldflags="-framework CoreServices" + ], + [have_cocoa_coreservices="no"]) + LIBS="$LIBS_save" + AC_MSG_CHECKING([whether Cocoa CoreServices framework is supported]) + AC_MSG_RESULT([${have_cocoa_coreservices}]) + AC_LANG_POP([Objective C]) + fi + ]) +fi +AC_SUBST(cocoa_coreservices_ldflags) + # Drm AC_ARG_ENABLE([drm], [AS_HELP_STRING([--enable-drm],[enable drm engine. @<:@default=disabled@:>@])], @@ -3969,6 +3990,20 @@ EFL_ADD_LIBS([EIO], [-lm]) ### Checks for linker characteristics ### Checks for library functions +have_inotify="${ac_cv_header_sys_inotify_h}" +AM_CONDITIONAL([HAVE_INOTIFY], [test "x${have_inotify}" = "xyes"]) + +have_notify_win32="${have_win32}" +AC_DEFINE_IF([HAVE_NOTIFY_WIN32], + [test "x${have_notify_win32}" = "xyes"], [1], + [File monitoring with Windows notification]) +AM_CONDITIONAL([HAVE_NOTIFY_WIN32], [test "x${have_notify_win32}" = "xyes"]) + +AC_DEFINE_IF([HAVE_NOTIFY_COCOA], + [test "x${have_darwin}" = "xyes"], [1], + [File monitoring with fsevent notification]) +AM_CONDITIONAL([HAVE_NOTIFY_COCOA], [test "x${have_darwin}" = "xyes"]) + EFL_LIB_END([Eio]) dnl TODO: remove these ifdefs from code! diff --git a/src/Makefile_Eio.am b/src/Makefile_Eio.am index d3144391a0..ee118a35ab 100644 --- a/src/Makefile_Eio.am +++ b/src/Makefile_Eio.am @@ -23,6 +23,10 @@ lib_eio_libeio_la_SOURCES += lib/eio/eio_monitor_inotify.c else if HAVE_NOTIFY_WIN32 lib_eio_libeio_la_SOURCES += lib/eio/eio_monitor_win32.c +else +if HAVE_NOTIFY_COCOA +lib_eio_libeio_la_SOURCES += lib/eio/eio_monitor_cocoa.c +endif endif endif @@ -30,6 +34,9 @@ lib_eio_libeio_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl @EIO_CFLAGS@ lib_eio_libeio_la_LIBADD = @EIO_LIBS@ lib_eio_libeio_la_DEPENDENCIES = @EIO_INTERNAL_LIBS@ lib_eio_libeio_la_LDFLAGS = @EFL_LTLIBRARY_FLAGS@ +if HAVE_NOTIFY_COCOA +lib_eio_libeio_la_LDFLAGS += -framework CoreServices +endif if EFL_ENABLE_TESTS diff --git a/src/lib/eio/Eio.h b/src/lib/eio/Eio.h index 0eb38c88b0..8edd4abc86 100644 --- a/src/lib/eio/Eio.h +++ b/src/lib/eio/Eio.h @@ -1219,13 +1219,13 @@ EAPI Eio_File *eio_eet_write_cipher(Eet_File *ef, EAPI extern int EIO_MONITOR_FILE_CREATED; /**< A new file was created in a watched directory */ EAPI extern int EIO_MONITOR_FILE_DELETED; /**< A watched file was deleted, or a file in a watched directory was deleted */ EAPI extern int EIO_MONITOR_FILE_MODIFIED; /**< A file was modified in a watched directory */ -EAPI extern int EIO_MONITOR_FILE_CLOSED; /**< A file was closed in a watched directory. This event is never sent on Windows */ +EAPI extern int EIO_MONITOR_FILE_CLOSED; /**< A file was closed in a watched directory. This event is never sent on Windows and OSX */ EAPI extern int EIO_MONITOR_DIRECTORY_CREATED; /**< A new directory was created in a watched directory */ EAPI extern int EIO_MONITOR_DIRECTORY_DELETED; /**< A directory has been deleted: this can be either a watched directory or one of its subdirectories */ EAPI extern int EIO_MONITOR_DIRECTORY_MODIFIED; /**< A directory has been modified in a watched directory */ -EAPI extern int EIO_MONITOR_DIRECTORY_CLOSED; /**< A directory has been closed in a watched directory. This event is never sent on Windows */ -EAPI extern int EIO_MONITOR_SELF_RENAME; /**< The monitored path has been renamed, an error could happen just after if the renamed path doesn't exist */ -EAPI extern int EIO_MONITOR_SELF_DELETED; /**< The monitored path has been removed */ +EAPI extern int EIO_MONITOR_DIRECTORY_CLOSED; /**< A directory has been closed in a watched directory. This event is never sent on Windows and OSX */ +EAPI extern int EIO_MONITOR_SELF_RENAME; /**< The monitored path has been renamed, an error could happen just after if the renamed path doesn't exist. This event is never sent on OSX */ +EAPI extern int EIO_MONITOR_SELF_DELETED; /**< The monitored path has been removed. This event is never sent on OSX */ EAPI extern int EIO_MONITOR_ERROR; /**< During operation the monitor failed and will no longer work. eio_monitor_del must be called on it. */ typedef struct _Eio_Monitor Eio_Monitor; diff --git a/src/lib/eio/eio_monitor_cocoa.c b/src/lib/eio/eio_monitor_cocoa.c new file mode 100644 index 0000000000..ea1d793bab --- /dev/null +++ b/src/lib/eio/eio_monitor_cocoa.c @@ -0,0 +1,405 @@ +/* EIO - EFL data type library + * Copyright (C) 2015 Enlightenment Developers: + * Pierre Lamot + * + * 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.1 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 . + */ + +#include "eio_private.h" +#include "Eio.h" + +#import + +static CFTimeInterval _latency = 0.1; + +/*============================================================================* + * Local * + *============================================================================*/ + +/** + * @cond LOCAL + */ + +struct _Eio_Monitor_Backend +{ + Eio_Monitor *parent; + ///the monitored path + char *mon_path; + ///the actual file path + char *real_path; +}; + +typedef struct _FSEvent_Info FSEvent_Info; + +struct _FSEvent_Info { + char *path; + FSEventStreamEventFlags flags; + +}; + +typedef struct _Eio_FSEvent_Table Eio_FSEvent_Table; + +struct _Eio_FSEvent_Table +{ + int mask; + int *ev_file_code; + int *ev_dir_code; +}; + +#define EIO_FSEVENT_LINE(FSe, Ef, Ed) \ + { kFSEventStreamEventFlag##FSe, &EIO_MONITOR_##Ef, &EIO_MONITOR_##Ed } + +static const Eio_FSEvent_Table match[] = { + EIO_FSEVENT_LINE(ItemChangeOwner, FILE_MODIFIED, DIRECTORY_MODIFIED), + EIO_FSEVENT_LINE(ItemInodeMetaMod, FILE_MODIFIED, DIRECTORY_MODIFIED), + EIO_FSEVENT_LINE(ItemXattrMod, FILE_MODIFIED, DIRECTORY_MODIFIED), + EIO_FSEVENT_LINE(ItemModified, FILE_MODIFIED, DIRECTORY_MODIFIED), + EIO_FSEVENT_LINE(ItemRemoved, FILE_DELETED, DIRECTORY_DELETED), + EIO_FSEVENT_LINE(ItemCreated, FILE_CREATED, DIRECTORY_CREATED), + EIO_FSEVENT_LINE(RootChanged, SELF_DELETED, SELF_DELETED) +}; + +static FSEventStreamRef _stream = NULL; +static Eina_Hash *_fsevent_monitors = NULL; +static CFMutableArrayRef _paths_to_watch = NULL; +static dispatch_queue_t _dispatch_queue; + +static Eina_Bool +_handle_fsevent_with_monitor(const Eina_Hash *hash EINA_UNUSED, + const void *key EINA_UNUSED, + void *data, + void *fdata) +{ + FSEvent_Info *event_info = (FSEvent_Info*)fdata; + Eio_Monitor_Backend *backend = (Eio_Monitor_Backend*)data; + FSEventStreamEventFlags flags = event_info->flags; + unsigned int i; + Eina_Bool is_dir; + unsigned int length, tmp_length; + + char *tmp = NULL; + + if (backend->parent->delete_me) + return 1; + + if (!eina_str_has_prefix(event_info->path, backend->real_path)) + { + return 1; + } + + length = strlen(event_info->path) - strlen(backend->real_path); + if (length == 0) + { + tmp = strdup(backend->parent->path); + } + else + { + tmp_length = + eina_stringshare_strlen(backend->parent->path) + length + 2; + + tmp = malloc(sizeof(char) * tmp_length); + snprintf(tmp, tmp_length, "%s/%s", + backend->parent->path, + &(event_info->path[strlen(backend->real_path) + 1])); + } + + is_dir = !!(flags & kFSEventStreamEventFlagItemIsDir); + + for (i = 0; i < sizeof (match) / sizeof (Eio_FSEvent_Table); ++i) + if (match[i].mask & flags) + { + DBG("send event from %s with event %X\n", event_info->path, flags); + _eio_monitor_send(backend->parent, + tmp, + is_dir ? *match[i].ev_dir_code : *match[i].ev_file_code); + } + + free(tmp); + //we have found the right event, no need to continue + return 0; +} + + +static void +_main_loop_send_event(void *data) +{ + FSEvent_Info *info = (FSEvent_Info*)data; + + if (!_stream) + { + //this can happen, when eio_shutdown is called + goto cleanup; + } + + if ((info->flags & kFSEventStreamEventFlagKernelDropped) != 0) + { + _eio_monitor_send(NULL, "", EIO_MONITOR_ERROR); + goto cleanup; + } + + eina_hash_foreach(_fsevent_monitors, + _handle_fsevent_with_monitor, + info); + + cleanup: + free(info->path); + free(info); +} + + +static void +_eio_fsevent_cb(ConstFSEventStreamRef stream_ref EINA_UNUSED, + void *ctx EINA_UNUSED, + size_t count, + void *event_paths, + const FSEventStreamEventFlags event_flags[], + const FSEventStreamEventId event_ids[] EINA_UNUSED + ) +{ + size_t i; + FSEvent_Info *event_info; + + for (i = 0; i < count; i++) + { + event_info = malloc(sizeof(FSEvent_Info)); + event_info->path = strdup(((char**)event_paths)[i]); + event_info->flags = event_flags[i]; + + ecore_main_loop_thread_safe_call_async(_main_loop_send_event, + event_info); + } +} + +static void +_eio_fsevent_del(void *data) +{ + Eio_Monitor_Backend *backend = (Eio_Monitor_Backend *)data; + free(backend); +} + +static void +_eio_get_monitor_path(const char *path, char **monpath, char **fullpath) +{ + char realPath[PATH_MAX]; + char *realPathOk; + char *dname = NULL; + char *fname = NULL; + struct stat sb; + + realPathOk = realpath(path, realPath); + if (realPathOk == NULL) + { + dname = dirname((char*)path); + if (strcmp(dname, ".") == 0) + { + realPathOk = realpath("./", realPath); + } + else + { + realPathOk = realpath(dname, realPath); + } + + if (realPathOk == NULL) + return; + } + + if (stat(realPath, &sb) < 0) + { + return; + } + + if (S_ISDIR(sb.st_mode)) + { + if (fullpath) + *fullpath = strdup(realPath); + if (monpath) + *monpath = strdup(realPath); + } + else + { + //not a directory, monitor parent + if (fullpath) + *fullpath = strdup(realPath); + dname = dirname(realPath); + if (monpath) + *monpath = strdup(dname); + } +} + +/** + * @endcond + */ + + +/*============================================================================* + * Global * + *============================================================================*/ + +/** + * @cond LOCAL + */ + +/** + * @endcond + */ + +void eio_monitor_backend_init(void) +{ + _dispatch_queue = dispatch_queue_create("org.elf.fseventqueue", NULL); + _fsevent_monitors = eina_hash_string_small_new(_eio_fsevent_del); + _paths_to_watch = CFArrayCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeArrayCallBacks); +} + +void eio_monitor_backend_shutdown(void) +{ + if (_stream) + { + FSEventStreamStop(_stream); + FSEventStreamInvalidate(_stream); + FSEventStreamRelease(_stream); + _stream = NULL; + } + dispatch_release(_dispatch_queue); + eina_hash_free(_fsevent_monitors); + CFRelease(_paths_to_watch); +} + + +void eio_monitor_backend_add(Eio_Monitor *monitor) +{ + Eio_Monitor_Backend *backend; + + FSEventStreamEventId eventid; + + CFStringRef path = NULL; + + //the path we should monitor + char *monitor_path = NULL; + //the real file path + char *real_path = NULL; + + _eio_get_monitor_path(monitor->path, &monitor_path, &real_path); + + backend = calloc(1, sizeof (Eio_Monitor_Backend)); + if (!backend) + { + free(monitor_path); + eio_monitor_fallback_add(monitor); + return; + } + + path = CFStringCreateWithCString(NULL, + monitor_path, + kCFStringEncodingUTF8); + + CFArrayAppendValue(_paths_to_watch, path); + + if (_stream) + { + eventid = FSEventStreamGetLatestEventId(_stream); + FSEventStreamRelease(_stream); + _stream = NULL; + } + else + { + eventid = kFSEventStreamEventIdSinceNow; + } + + _stream = FSEventStreamCreate(NULL, + _eio_fsevent_cb, + NULL, + _paths_to_watch, + eventid, + _latency, + kFSEventStreamCreateFlagFileEvents + | kFSEventStreamCreateFlagNoDefer + ); + + if (!_stream) + { + eio_monitor_fallback_add(monitor); + return; + } + + backend->parent = monitor; + backend->mon_path = monitor_path; + backend->real_path = real_path; + monitor->backend = backend; + + eina_hash_direct_add(_fsevent_monitors, monitor->path, backend); + + FSEventStreamSetDispatchQueue(_stream, _dispatch_queue); + FSEventStreamStart(_stream); + + +} + +void eio_monitor_backend_del(Eio_Monitor *monitor) +{ + Eio_Monitor_Backend *backend; + CFStringRef path = NULL; + FSEventStreamEventId eventid; + char *monitor_path; + + if (!_stream) + { + eio_monitor_fallback_del(monitor); + return; + } + + _eio_get_monitor_path(monitor->path, &monitor_path, NULL); + + eventid = FSEventStreamGetLatestEventId(_stream); + FSEventStreamRelease(_stream); + _stream = NULL; + + path = CFStringCreateWithCString(NULL, + monitor_path, + kCFStringEncodingUTF8); + + CFIndex idx = + CFArrayGetFirstIndexOfValue(_paths_to_watch, + CFRangeMake(0, + CFArrayGetCount(_paths_to_watch) + ), + path); + + if (idx != -1) + { + CFArrayRemoveValueAtIndex(_paths_to_watch, idx); + } + + _stream = FSEventStreamCreate(NULL, + _eio_fsevent_cb, + NULL, + _paths_to_watch, + eventid, + _latency, + kFSEventStreamCreateFlagFileEvents + | kFSEventStreamCreateFlagNoDefer + ); + backend = monitor->backend; + monitor->backend = NULL; + if (!backend) return; + + eina_hash_del(_fsevent_monitors, monitor->path, backend); +} + + +/*============================================================================* + * API * + *============================================================================*/ diff --git a/src/lib/eio/eio_monitor_poll.c b/src/lib/eio/eio_monitor_poll.c index c93ac4d579..0e222f5bc8 100644 --- a/src/lib/eio/eio_monitor_poll.c +++ b/src/lib/eio/eio_monitor_poll.c @@ -270,7 +270,7 @@ _eio_monitor_fallback_timer_cb(void *data) * @cond LOCAL */ -#if !defined HAVE_SYS_INOTIFY_H && !defined HAVE_NOTIFY_WIN32 +#if !defined HAVE_SYS_INOTIFY_H && !defined HAVE_NOTIFY_WIN32 && !defined HAVE_NOTIFY_COCOA void eio_monitor_backend_init(void) { } diff --git a/src/tests/eio/eio_test_monitor.c b/src/tests/eio/eio_test_monitor.c index c1f10a45b1..378c427a3c 100644 --- a/src/tests/eio/eio_test_monitor.c +++ b/src/tests/eio/eio_test_monitor.c @@ -17,9 +17,9 @@ #define TEST_TIMEOUT_SEC 10 #define TEST_OPERATION_DELAY 0.5 -static Ecore_Timer * test_timeout_timer; +static Ecore_Timer *test_timeout_timer; -static Eina_Bool _test_timeout_cb(void* data EINA_UNUSED) +static Eina_Bool _test_timeout_cb(void *data EINA_UNUSED) { ck_abort_msg("test timeout"); ecore_main_loop_quit(); @@ -35,16 +35,26 @@ static void _cancel_timeout() } } +static Eina_Bool _test_timeout_expected(void *data EINA_UNUSED) +{ + if (test_timeout_timer != NULL) + { + _cancel_timeout(); + } + ecore_main_loop_quit(); + return ECORE_CALLBACK_CANCEL; +} + ///////////////// file and directory operations typedef struct { - const char* src; - const char* dst; + const char *src; + const char *dst; } RenameOperation; -static Eina_Bool _delete_directory(void* data) +static Eina_Bool _delete_directory(void *data) { - const char* dirname = (const char*)data; + const char *dirname = (const char*)data; if (ecore_file_is_dir(dirname)) { ecore_file_recursive_rm(dirname); @@ -52,40 +62,40 @@ static Eina_Bool _delete_directory(void* data) return ECORE_CALLBACK_CANCEL; } -static Eina_Bool _create_directory(void* data) +static Eina_Bool _create_directory(void *data) { - const char* dirname = (const char*)data; + const char *dirname = (const char*)data; ecore_file_mkpath(dirname); return ECORE_CALLBACK_CANCEL; } -static Eina_Bool _create_file(void* data) +static Eina_Bool _create_file(void *data) { - FILE* fd = fopen((const char*)data, "w+"); + FILE *fd = fopen((const char*)data, "w+"); ck_assert_ptr_ne(fd, NULL); fprintf(fd, "test test"); fclose(fd); return ECORE_CALLBACK_CANCEL; } -static Eina_Bool _delete_file(void* data) +static Eina_Bool _delete_file(void *data) { Eina_Bool file_removed = ecore_file_remove((const char*)data); ck_assert(file_removed); return ECORE_CALLBACK_CANCEL; } -static Eina_Bool _modify_file(void* data) +static Eina_Bool _modify_file(void *data) { - FILE* fd = fopen((const char*)data, "a"); + FILE *fd = fopen((const char*)data, "a"); ck_assert_ptr_ne(fd, NULL); fprintf(fd, "appened"); fclose(fd); return ECORE_CALLBACK_CANCEL; } -static Eina_Bool _modify_attrib_file(void* data) +static Eina_Bool _modify_attrib_file(void *data) { int ret = chmod((const char*)data, 0666); ck_assert_int_eq(ret, 0); @@ -94,16 +104,17 @@ static Eina_Bool _modify_attrib_file(void* data) /////// helper functions -static void _check_event_path(void* data, void* event) +static Eina_Bool _check_event_path(void *data, void *event) { - const char* expected_path = ecore_file_realpath((const char*)data); - const char* actual_path = ecore_file_realpath(((Eio_Monitor_Event*)event)->filename); - ck_assert_str_eq(actual_path, expected_path); + const char *expected_path = (const char*)data; + const char *actual_path = ((Eio_Monitor_Event*)event)->filename; + ck_assert_str_eq((const char*)data, ((Eio_Monitor_Event*)event)->filename); + return EINA_TRUE; } -static Eina_Tmpstr* _common_init() +static Eina_Tmpstr *_common_init() { - Eina_Tmpstr* dirname; + Eina_Tmpstr *dirname; fail_if(eio_init() != 1); ecore_file_init(); @@ -114,7 +125,7 @@ static Eina_Tmpstr* _common_init() return dirname; } -static void _common_shutdown(Eina_Tmpstr* dirname) +static void _common_shutdown(Eina_Tmpstr *dirname) { _delete_directory((void*)dirname); ecore_file_shutdown(); @@ -124,21 +135,26 @@ static void _common_shutdown(Eina_Tmpstr* dirname) /////// tests monitoring a directory -static void _file_created_cb(void* data, int type, void* event) +static void _file_created_cb(void *data, int type, void *event) { ck_assert_int_eq(type, (int)EIO_MONITOR_FILE_CREATED); - _check_event_path(data, event); - _cancel_timeout(); - ecore_main_loop_quit(); + if (_check_event_path(data, event)) + { + _cancel_timeout(); + ecore_main_loop_quit(); + } } START_TEST(eio_test_monitor_directory_file_created_notify) { - Eina_Tmpstr* dirname = _common_init(); - Eina_Stringshare* filename; + Eina_Tmpstr *dirname = _common_init(); + Eina_Stringshare *filename; - filename = eina_stringshare_printf("%s/filecreated", dirname); + filename = eina_stringshare_printf("%s/eio_test_monitor_directory_file_created_notify", dirname); + + //sleep to avoid catching event generated by above manipulations + usleep(500000); //monitor directory eio_monitor_add(dirname); @@ -152,22 +168,27 @@ START_TEST(eio_test_monitor_directory_file_created_notify) } END_TEST -static void _file_deleted_cb(void* data, int type, void* event) +static void _file_deleted_cb(void *data, int type, void *event) { ck_assert_int_eq(type, (int)EIO_MONITOR_FILE_DELETED); - _check_event_path(data, event); - _cancel_timeout(); - ecore_main_loop_quit(); + if (_check_event_path(data, event)) + { + _cancel_timeout(); + ecore_main_loop_quit(); + } } START_TEST(eio_test_monitor_directory_file_deleted_notify) { - Eina_Tmpstr* dirname = _common_init(); - Eina_Stringshare* filename; + Eina_Tmpstr *dirname = _common_init(); + Eina_Stringshare *filename; - filename = eina_stringshare_printf("%s/filecreated", dirname); + filename = eina_stringshare_printf("%s/eio_test_monitor_directory_file_deleted_notify", dirname); _create_file((void*)filename); + //sleep to avoid catching event generated by above manipulations + usleep(500000); + //monitor directory eio_monitor_add(dirname); ecore_event_handler_add(EIO_MONITOR_FILE_DELETED, (Ecore_Event_Handler_Cb)_file_deleted_cb, filename); @@ -180,22 +201,27 @@ START_TEST(eio_test_monitor_directory_file_deleted_notify) } END_TEST -static void _file_modified_cb(void* data, int type, void* event) +static void _file_modified_cb(void *data, int type, void *event) { ck_assert_int_eq(type, (int)EIO_MONITOR_FILE_MODIFIED); - _check_event_path(data, event); - _cancel_timeout(); - ecore_main_loop_quit(); + if(_check_event_path(data, event)) + { + _cancel_timeout(); + ecore_main_loop_quit(); + } } START_TEST(eio_test_monitor_directory_file_modified_notify) { - Eina_Tmpstr* dirname = _common_init(); - Eina_Stringshare* filename; + Eina_Tmpstr *dirname = _common_init(); + Eina_Stringshare *filename; - filename = eina_stringshare_printf("%s/filecreated", dirname); + filename = eina_stringshare_printf("%s/eio_test_monitor_directory_file_modified_notify", dirname); _create_file((void*)filename); + //sleep to avoid catching event generated by above manipulations + usleep(500000); + //monitor directory eio_monitor_add(dirname); ecore_event_handler_add(EIO_MONITOR_FILE_MODIFIED, (Ecore_Event_Handler_Cb)_file_modified_cb, filename); @@ -209,22 +235,27 @@ START_TEST(eio_test_monitor_directory_file_modified_notify) } END_TEST -static void _file_closed_cb(void* data, int type, void* event) +static void _file_closed_cb(void *data, int type, void *event) { ck_assert_int_eq(type, (int)EIO_MONITOR_FILE_CLOSED); - _check_event_path(data, event); - _cancel_timeout(); - ecore_main_loop_quit(); + if (_check_event_path(data, event)) + { + _cancel_timeout(); + ecore_main_loop_quit(); + } } START_TEST(eio_test_monitor_directory_file_closed_notify) { - Eina_Tmpstr* dirname = _common_init(); - Eina_Stringshare* filename; + Eina_Tmpstr *dirname = _common_init(); + Eina_Stringshare *filename; - filename = eina_stringshare_printf("%s/filecreated", dirname); + filename = eina_stringshare_printf("%s/eio_test_monitor_directory_file_closed_notify", dirname); _create_file((void*)filename); + //sleep to avoid catching event generated by above manipulations + usleep(500000); + //monitor directory eio_monitor_add(dirname); ecore_event_handler_add(EIO_MONITOR_FILE_CLOSED, (Ecore_Event_Handler_Cb)_file_closed_cb, filename); @@ -236,20 +267,25 @@ START_TEST(eio_test_monitor_directory_file_closed_notify) } END_TEST -static void _directory_created_cb(void* data, int type, void* event) +static void _directory_created_cb(void *data, int type, void *event) { ck_assert_int_eq(type, (int)EIO_MONITOR_DIRECTORY_CREATED); - _check_event_path(data, event); - _cancel_timeout(); - ecore_main_loop_quit(); + if (_check_event_path(data, event)) + { + _cancel_timeout(); + ecore_main_loop_quit(); + } } START_TEST(eio_test_monitor_directory_directory_created_notify) { - Eina_Tmpstr* dirname = _common_init(); - Eina_Stringshare* filename; + Eina_Tmpstr *dirname = _common_init(); + Eina_Stringshare *filename; - filename = eina_stringshare_printf("%s/dircreated", dirname); + filename = eina_stringshare_printf("%s/eio_test_monitor_directory_directory_created_notify", dirname); + + //sleep to avoid catching event generated by above manipulations + usleep(500000); //monitor directory eio_monitor_add(dirname); @@ -263,22 +299,27 @@ START_TEST(eio_test_monitor_directory_directory_created_notify) } END_TEST -static void _directory_deleted_cb(void* data, int type, void* event) +static void _directory_deleted_cb(void *data, int type, void *event) { ck_assert_int_eq(type, (int)EIO_MONITOR_DIRECTORY_DELETED); - _check_event_path(data, event); - _cancel_timeout(); - ecore_main_loop_quit(); + if (_check_event_path(data, event)) + { + _cancel_timeout(); + ecore_main_loop_quit(); + } } START_TEST(eio_test_monitor_directory_directory_deleted_notify) { - Eina_Tmpstr* dirname = _common_init(); - Eina_Stringshare* filename; + Eina_Tmpstr *dirname = _common_init(); + Eina_Stringshare *filename; - filename = eina_stringshare_printf("%s/dircreated", dirname); + filename = eina_stringshare_printf("%s/eio_test_monitor_directory_directory_deleted_notify", dirname); _create_directory((void*)filename); + //sleep to avoid catching event generated by above manipulations + usleep(500000); + //monitor directory eio_monitor_add(dirname); ecore_event_handler_add(EIO_MONITOR_DIRECTORY_DELETED, (Ecore_Event_Handler_Cb)_directory_deleted_cb, filename); @@ -291,23 +332,28 @@ START_TEST(eio_test_monitor_directory_directory_deleted_notify) } END_TEST -static void _directory_modified_cb(void* data, int type, void* event) +static void _directory_modified_cb(void *data, int type, void *event) { ck_assert_int_eq(type, (int)EIO_MONITOR_DIRECTORY_MODIFIED); - _check_event_path(data, event); - _cancel_timeout(); - ecore_main_loop_quit(); + if (_check_event_path(data, event)) + { + _cancel_timeout(); + ecore_main_loop_quit(); + } } START_TEST(eio_test_monitor_directory_directory_modified_notify) { - Eina_Tmpstr* dirname = _common_init(); - Eina_Stringshare* filename; + Eina_Tmpstr *dirname = _common_init(); + Eina_Stringshare *filename; - filename = eina_stringshare_printf("%s/filecreated", dirname); + filename = eina_stringshare_printf("%s/eio_test_monitor_directory_directory_modified_notify", dirname); _create_directory((void*)filename); + //sleep to avoid catching event generated by above manipulations + usleep(500000); + //monitor directory eio_monitor_add(dirname); ecore_event_handler_add(EIO_MONITOR_DIRECTORY_MODIFIED, (Ecore_Event_Handler_Cb)_directory_modified_cb, filename); @@ -321,18 +367,23 @@ START_TEST(eio_test_monitor_directory_directory_modified_notify) END_TEST -static void _directory_self_deleted_cb(void* data, int type, void* event) +static void _directory_self_deleted_cb(void *data, int type, void *event) { ck_assert_int_eq(type, (int)EIO_MONITOR_SELF_DELETED); - _check_event_path(data, event); - _cancel_timeout(); - ecore_main_loop_quit(); + if (_check_event_path(data, event)) + { + _cancel_timeout(); + ecore_main_loop_quit(); + } } START_TEST(eio_test_monitor_directory_directory_self_deleted_notify) { - Eina_Tmpstr* dirname = _common_init(); + Eina_Tmpstr *dirname = _common_init(); + + //sleep to avoid catching event generated by above manipulations + usleep(500000); //monitor directory eio_monitor_add(dirname); @@ -350,12 +401,15 @@ END_TEST START_TEST(eio_test_monitor_file_file_modified_notify) { - Eina_Tmpstr* dirname = _common_init(); - Eina_Stringshare* filename; + Eina_Tmpstr *dirname = _common_init(); + Eina_Stringshare *filename; filename = eina_stringshare_printf("%s/filecreated", dirname); _create_file((void*)filename); + //sleep to avoid catching event generated by above manipulations + usleep(500000); + //monitor file eio_monitor_add(filename); ecore_event_handler_add(EIO_MONITOR_FILE_MODIFIED, (Ecore_Event_Handler_Cb)_file_modified_cb, filename); @@ -371,12 +425,15 @@ END_TEST START_TEST(eio_test_monitor_file_file_attrib_modified_notify) { - Eina_Tmpstr* dirname = _common_init(); - Eina_Stringshare* filename; + Eina_Tmpstr *dirname = _common_init(); + Eina_Stringshare *filename; - filename = eina_stringshare_printf("%s/filecreated", dirname); + filename = eina_stringshare_printf("%s/eio_test_monitor_file_file_attrib_modified_notify", dirname); _create_file((void*)filename); + //sleep to avoid catching event generated by above manipulations + usleep(500000); + //monitor file eio_monitor_add(filename); ecore_event_handler_add(EIO_MONITOR_FILE_MODIFIED, (Ecore_Event_Handler_Cb)_file_modified_cb, filename); @@ -392,12 +449,15 @@ END_TEST START_TEST(eio_test_monitor_file_file_closed_notify) { - Eina_Tmpstr* dirname = _common_init(); - Eina_Stringshare* filename; + Eina_Tmpstr *dirname = _common_init(); + Eina_Stringshare *filename; - filename = eina_stringshare_printf("%s/filecreated", dirname); + filename = eina_stringshare_printf("%s/eio_test_monitor_file_file_closed_notify", dirname); _create_file((void*)filename); + //sleep to avoid catching event generated by above manipulations + usleep(500000); + //monitor file eio_monitor_add(dirname); ecore_event_handler_add(EIO_MONITOR_FILE_CLOSED, (Ecore_Event_Handler_Cb)_file_closed_cb, filename); @@ -411,12 +471,15 @@ END_TEST START_TEST(eio_test_monitor_file_file_self_deleted_notify) { - Eina_Tmpstr* dirname = _common_init(); - Eina_Stringshare* filename; + Eina_Tmpstr *dirname = _common_init(); + Eina_Stringshare *filename; - filename = eina_stringshare_printf("%s/filecreated", dirname); + filename = eina_stringshare_printf("%s/eio_test_monitor_file_file_self_deleted_notify", dirname); _create_file((void*)filename); + //sleep to avoid catching event generated by above manipulations + usleep(500000); + //monitor file eio_monitor_add(filename); ecore_event_handler_add(EIO_MONITOR_SELF_DELETED, (Ecore_Event_Handler_Cb)_directory_self_deleted_cb, filename); @@ -429,24 +492,189 @@ START_TEST(eio_test_monitor_file_file_self_deleted_notify) } END_TEST +START_TEST(eio_test_monitor_two_monitors_one_event) +{ + Eina_Tmpstr *dirname = _common_init(); + Eina_Tmpstr *dirname2; + + Eina_Stringshare *filename; + + eina_file_mkdtemp("checkFileCreationXXXXXX", &dirname2); + filename = eina_stringshare_printf("%s/eio_test_monitor_two_monitors_one_event", dirname); + + //sleep to avoid catching event generated by above manipulations + usleep(500000); + + //monitor directory + eio_monitor_add(dirname); + eio_monitor_add(dirname2); + ecore_event_handler_add(EIO_MONITOR_FILE_CREATED, (Ecore_Event_Handler_Cb)_file_created_cb, filename); + + ecore_timer_add(TEST_OPERATION_DELAY, _create_file, filename); + + ecore_main_loop_begin(); + + _delete_directory((void*)dirname2); + _common_shutdown(dirname); +} +END_TEST + + +START_TEST(eio_test_monitor_two_monitors_one_removed_one_event) +{ + Eina_Tmpstr *dirname = _common_init(); + Eina_Tmpstr *dirname2; + + Eina_Stringshare *filename; + + Eio_Monitor *monitor; + + eina_file_mkdtemp("checkFileCreationXXXXXX", &dirname2); + filename = eina_stringshare_printf("%s/eio_test_monitor_two_monitors_one_removed", dirname); + + //sleep to avoid catching event generated by above manipulations + usleep(500000); + + //monitor directory + monitor = eio_monitor_add(dirname2); + eio_monitor_add(dirname); + eio_monitor_del(monitor); + ecore_event_handler_add(EIO_MONITOR_FILE_CREATED, (Ecore_Event_Handler_Cb)_file_created_cb, filename); + + ecore_timer_add(TEST_OPERATION_DELAY, _create_file, filename); + + ecore_main_loop_begin(); + + _delete_directory((void*)dirname2); + _common_shutdown(dirname); +} +END_TEST + +static void _unexpected_event_cb(void *data, int type, void *event) +{ + ck_abort_msg("unexpected event"); +} + +START_TEST(eio_test_monitor_two_monitors_one_removed_no_event) +{ + Eina_Tmpstr *dirname = _common_init(); + Eina_Tmpstr *dirname2; + + Eina_Stringshare *filename; + + Eio_Monitor *monitor; + + eina_file_mkdtemp("checkFileCreationXXXXXX", &dirname2); + filename = eina_stringshare_printf("%s/eio_test_monitor_two_monitors_one_removed", dirname); + + //sleep to avoid catching event generated by above manipulations + usleep(500000); + + //monitor directory + monitor = eio_monitor_add(dirname); + eio_monitor_add(dirname2); + eio_monitor_del(monitor); + ecore_event_handler_add(EIO_MONITOR_FILE_CREATED, (Ecore_Event_Handler_Cb)_unexpected_event_cb, filename); + + ecore_timer_add(TEST_OPERATION_DELAY, _create_file, filename); + ecore_timer_add(TEST_TIMEOUT_SEC - 1, _test_timeout_expected, NULL); + + ecore_main_loop_begin(); + + _delete_directory((void*)dirname2); + _common_shutdown(dirname); +} +END_TEST + +START_TEST(eio_test_monitor_two_files_in_same_directory) +{ + Eina_Tmpstr *dirname = _common_init(); + Eina_Stringshare *filename; + Eina_Stringshare *filename2; + + filename = eina_stringshare_printf("%s/eio_test_monitor_two_files_in_same_directory_1", dirname); + filename2 = eina_stringshare_printf("%s/eio_test_monitor_two_files_in_same_directory_2", dirname); + _create_file((void*)filename); + _create_file((void*)filename2); + + //sleep to avoid catching event generated by above manipulations + usleep(500000); + + //monitor file + eio_monitor_add(filename); + eio_monitor_add(filename2); + ecore_event_handler_add(EIO_MONITOR_FILE_MODIFIED, (Ecore_Event_Handler_Cb)_file_modified_cb, filename); + + ecore_timer_add(TEST_OPERATION_DELAY, _modify_file, filename); + + ecore_main_loop_begin(); + + //cleanup + _common_shutdown(dirname); +} +END_TEST + + +START_TEST(eio_test_monitor_two_files_in_same_directory_one_removed) +{ + Eina_Tmpstr *dirname = _common_init(); + Eina_Stringshare *filename; + Eina_Stringshare *filename2; + Eio_Monitor *monitor; + + filename = eina_stringshare_printf("%s/eio_test_monitor_two_files_in_same_directory_one_removed_1", dirname); + filename2 = eina_stringshare_printf("%s/eio_test_monitor_two_files_in_same_directory_one_removed_2", dirname); + _create_file((void*)filename); + _create_file((void*)filename2); + + //sleep to avoid catching event generated by above manipulations + usleep(500000); + + //monitor file + monitor = eio_monitor_add(filename); + eio_monitor_add(filename2); + eio_monitor_del(monitor); + + ecore_event_handler_add(EIO_MONITOR_FILE_MODIFIED, (Ecore_Event_Handler_Cb)_unexpected_event_cb, filename); + + ecore_timer_add(TEST_OPERATION_DELAY, _modify_file, filename); + ecore_timer_add(TEST_TIMEOUT_SEC - 1, _test_timeout_expected, NULL); + + ecore_main_loop_begin(); + + //cleanup + _common_shutdown(dirname); +} +END_TEST + + void eio_test_monitor(TCase *tc) { tcase_add_test(tc, eio_test_monitor_directory_file_created_notify); tcase_add_test(tc, eio_test_monitor_directory_file_deleted_notify); tcase_add_test(tc, eio_test_monitor_directory_file_modified_notify); -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(__MACH__) tcase_add_test(tc, eio_test_monitor_directory_file_closed_notify); #endif tcase_add_test(tc, eio_test_monitor_directory_directory_created_notify); tcase_add_test(tc, eio_test_monitor_directory_directory_deleted_notify); tcase_add_test(tc, eio_test_monitor_directory_directory_modified_notify); +#ifndef __MACH__ tcase_add_test(tc, eio_test_monitor_directory_directory_self_deleted_notify); +#endif tcase_add_test(tc, eio_test_monitor_file_file_modified_notify); tcase_add_test(tc, eio_test_monitor_file_file_attrib_modified_notify); -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(__MACH__) tcase_add_test(tc, eio_test_monitor_file_file_closed_notify); #endif +#ifndef __MACH__ tcase_add_test(tc, eio_test_monitor_file_file_self_deleted_notify); +#endif + tcase_add_test(tc, eio_test_monitor_two_monitors_one_event); + tcase_add_test(tc, eio_test_monitor_two_monitors_one_removed_one_event); + tcase_add_test(tc, eio_test_monitor_two_monitors_one_removed_no_event); + tcase_add_test(tc, eio_test_monitor_two_files_in_same_directory); + tcase_add_test(tc, eio_test_monitor_two_files_in_same_directory_one_removed); }