mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-31 07:33:01 +00:00 
			
		
		
		
	Add testing for Linux.
This commit is contained in:
		| @@ -53,17 +53,6 @@ typedef struct { | ||||
|     int is_watching; | ||||
| } JanetWatcher; | ||||
|  | ||||
| /* Reject certain filename events without sending anything to the channel | ||||
|  * to make things faster and not waste time and memory creating events. This | ||||
|  * should also let us watch only certain file names, patterns, etc. */ | ||||
| static int janet_watch_filter(JanetWatcher *watcher, Janet filename, int wd) { | ||||
|     /* TODO - add filtering */ | ||||
|     (void) watcher; | ||||
|     (void) filename; | ||||
|     (void) wd; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| #ifdef JANET_LINUX | ||||
|  | ||||
| #include <sys/inotify.h> | ||||
| @@ -78,7 +67,7 @@ static const JanetWatchFlagName watcher_flags_linux[] = { | ||||
|     {"create", IN_CREATE}, | ||||
|     {"delete", IN_DELETE}, | ||||
|     {"delete-self", IN_DELETE_SELF}, | ||||
|     {"ignored", IN_OPEN}, | ||||
|     {"ignored", IN_IGNORED}, | ||||
|     {"modify", IN_MODIFY}, | ||||
|     {"move-self", IN_MOVE_SELF}, | ||||
|     {"moved-from", IN_MOVED_FROM}, | ||||
| @@ -153,7 +142,7 @@ static void janet_watcher_remove(JanetWatcher *watcher, const char *path) { | ||||
|  | ||||
| static void watcher_callback_read(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|     JanetStream *stream = fiber->ev_stream; | ||||
|     JanetWatcher *watcher = (JanetWatcher *) fiber->ev_state; | ||||
|     JanetWatcher *watcher = *((JanetWatcher **) fiber->ev_state); | ||||
|     char buf[1024]; | ||||
|     switch (event) { | ||||
|         default: | ||||
| @@ -163,13 +152,11 @@ static void watcher_callback_read(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|             break; | ||||
|         case JANET_ASYNC_EVENT_CLOSE: | ||||
|             janet_schedule(fiber, janet_wrap_nil()); | ||||
|             fiber->ev_state = NULL; | ||||
|             janet_async_end(fiber); | ||||
|             break; | ||||
|         case JANET_ASYNC_EVENT_ERR: | ||||
|             { | ||||
|                 janet_schedule(fiber, janet_wrap_nil()); | ||||
|                 fiber->ev_state = NULL; | ||||
|                 janet_async_end(fiber); | ||||
|                 break; | ||||
|             } | ||||
| @@ -221,9 +208,6 @@ static void watcher_callback_read(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|                         cursor += inevent.len; | ||||
|                     } | ||||
|  | ||||
|                     /* Filter events by pattern */ | ||||
|                     if (!janet_watch_filter(watcher, name, inevent.wd)) continue; | ||||
|  | ||||
|                     /* Got an event */ | ||||
|                     Janet path = janet_table_get(watcher->watch_descriptors, janet_wrap_integer(inevent.wd)); | ||||
|                     JanetKV *event = janet_struct_begin(6); | ||||
| @@ -254,13 +238,17 @@ static void janet_watcher_listen(JanetWatcher *watcher) { | ||||
|     watcher->is_watching = 1; | ||||
|     JanetFunction *thunk = janet_thunk_delay(janet_wrap_nil()); | ||||
|     JanetFiber *fiber = janet_fiber(thunk, 64, 0, NULL); | ||||
|     janet_async_start_fiber(fiber, watcher->stream, JANET_ASYNC_LISTEN_READ, watcher_callback_read, watcher); | ||||
|     JanetWatcher **state = janet_malloc(sizeof(JanetWatcher *)); /* Gross */ | ||||
|     *state = watcher; | ||||
|     janet_async_start_fiber(fiber, watcher->stream, JANET_ASYNC_LISTEN_READ, watcher_callback_read, state); | ||||
|     janet_gcroot(janet_wrap_abstract(watcher)); | ||||
| } | ||||
|  | ||||
| static void janet_watcher_unlisten(JanetWatcher *watcher) { | ||||
|     if (!watcher->is_watching) return; | ||||
|     watcher->is_watching = 0; | ||||
|     janet_stream_close(watcher->stream); | ||||
|     janet_gcunroot(janet_wrap_abstract(watcher)); | ||||
| } | ||||
|  | ||||
| #elif JANET_WINDOWS | ||||
| @@ -268,6 +256,16 @@ static void janet_watcher_unlisten(JanetWatcher *watcher) { | ||||
| #define WATCHFLAG_RECURSIVE 0x100000u | ||||
|  | ||||
| static const JanetWatchFlagName watcher_flags_windows[] = { | ||||
|     {"all",  | ||||
|         FILE_NOTIFY_CHANGE_ATTRIBUTES | | ||||
|         FILE_NOTIFY_CHANGE_CREATION | | ||||
|         FILE_NOTIFY_CHANGE_DIR_NAME | | ||||
|         FILE_NOTIFY_CHANGE_FILE_NAME | | ||||
|         FILE_NOTIFY_CHANGE_LAST_ACCESS | | ||||
|         FILE_NOTIFY_CHANGE_LAST_WRITE | | ||||
|         FILE_NOTIFY_CHANGE_SECURITY | | ||||
|         FILE_NOTIFY_CHANGE_SIZE | | ||||
|         WATCHFLAG_RECURSIVE}, | ||||
|     {"attributes", FILE_NOTIFY_CHANGE_ATTRIBUTES}, | ||||
|     {"creation", FILE_NOTIFY_CHANGE_CREATION}, | ||||
|     {"dir-name", FILE_NOTIFY_CHANGE_DIR_NAME}, | ||||
| @@ -392,7 +390,7 @@ static void watcher_callback_read(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|                     } | ||||
|  | ||||
|                     JanetKV *event = janet_struct_begin(3); | ||||
|                     janet_struct_put(event, janet_ckeywordv("action"), janet_ckeywordv(watcher_actions_windows[fni->Action])); | ||||
|                     janet_struct_put(event, janet_ckeywordv("type"), janet_ckeywordv(watcher_actions_windows[fni->Action])); | ||||
|                     janet_struct_put(event, janet_ckeywordv("file-name"), filename); | ||||
|                     janet_struct_put(event, janet_ckeywordv("dir"), janet_wrap_string(ow->dir_path)); | ||||
|                     Janet eventv = janet_wrap_struct(janet_struct_end(event)); | ||||
| @@ -470,6 +468,7 @@ static void janet_watcher_listen(JanetWatcher *watcher) { | ||||
|         OverlappedWatch *ow = janet_unwrap_pointer(kv->value); | ||||
|         start_listening_ow(ow); | ||||
|     } | ||||
|     janet_gcroot(janet_wrap_abstract(watcher)); | ||||
| } | ||||
|  | ||||
| static void janet_watcher_unlisten(JanetWatcher *watcher) { | ||||
| @@ -482,6 +481,7 @@ static void janet_watcher_unlisten(JanetWatcher *watcher) { | ||||
|         janet_stream_close(ow->stream); | ||||
|     } | ||||
|     janet_table_clear(watcher->watch_descriptors); | ||||
|     janet_gcunroot(janet_wrap_abstract(watcher)); | ||||
| } | ||||
|  | ||||
| #else | ||||
|   | ||||
| @@ -24,6 +24,8 @@ | ||||
| (assert true) | ||||
|  | ||||
| (def chan (ev/chan 1000)) | ||||
| (def is-win (= :windows (os/which))) | ||||
| (def is-linux (= :linux (os/which))) | ||||
|  | ||||
| # Test GC | ||||
| (assert-no-error "filewatch/new" (filewatch/new chan)) | ||||
| @@ -36,7 +38,7 @@ | ||||
|     (def event (ev/take chan)) | ||||
|     (when is-verbose (pp event)) | ||||
|     (assert event "check event") | ||||
|     (assert (= value (get event key)) (string/format "got %p, exepcted %p" (get event key) value)))) | ||||
|     (assert (= value (get event key)) (string/format "got %p, expected %p" (get event key) value)))) | ||||
|  | ||||
| (defn- expect-empty | ||||
|   [] | ||||
| @@ -49,57 +51,121 @@ | ||||
|   (def path (string dir "/" name)) | ||||
|   (spit path "test text")) | ||||
|  | ||||
| # Different operating systems report events differently. While it would be nice to | ||||
| # normalize this, each system has very large limitations in what can be reported when | ||||
| # compared with other systems. As such, the maximum subset of common functionality here | ||||
| # is quite small. Instead, test the capabilities of each system. | ||||
|  | ||||
| # Create a file watcher on two test directories | ||||
| (def fw (filewatch/new chan)) | ||||
| (def td1 (randdir)) | ||||
| (def td2 (randdir)) | ||||
| (rmrf td1) | ||||
| (rmrf td2) | ||||
| (os/mkdir td1) | ||||
| (os/mkdir td2) | ||||
| (filewatch/add fw td1 :last-write :last-access :file-name :dir-name :size :attributes :recursive) | ||||
| (filewatch/add fw td2 :last-write :last-access :file-name :dir-name :size :attributes) | ||||
| (case (os/which) | ||||
|   :windows | ||||
|   (do | ||||
|     (filewatch/add fw td1 :last-write :last-access :file-name :dir-name :size :attributes :recursive) | ||||
|     (filewatch/add fw td2 :last-write :last-access :file-name :dir-name :size :attributes)) | ||||
|   # default | ||||
|   (do | ||||
|     (filewatch/add fw td1 :close-write :create :delete) | ||||
|     (filewatch/add fw td2 :close-write :create :delete :ignored))) | ||||
| (assert-no-error "filewatch/listen no error" (filewatch/listen fw)) | ||||
|  | ||||
| # Simulate some file event | ||||
| (spit-file td1 "file1.txt") | ||||
| (expect :action :added) | ||||
| (expect :action :modified) | ||||
| (expect-empty) | ||||
| (gccollect) | ||||
| (spit-file td1 "file1.txt") | ||||
| (expect :action :modified) | ||||
| (expect :action :modified) | ||||
| (expect-empty) | ||||
| (gccollect) | ||||
| # | ||||
| # Windows file writing | ||||
| # | ||||
|  | ||||
| # Check td2 | ||||
| (spit-file td2 "file2.txt") | ||||
| (expect :action :added) | ||||
| (expect :action :modified) | ||||
| (expect-empty) | ||||
| (when is-win | ||||
|   (spit-file td1 "file1.txt") | ||||
|   (expect :type :added) | ||||
|   (expect :type :modified) | ||||
|   (expect-empty) | ||||
|   (gccollect) | ||||
|   (spit-file td1 "file1.txt") | ||||
|   (expect :type :modified) | ||||
|   (expect :type :modified) | ||||
|   (expect-empty) | ||||
|   (gccollect) | ||||
|  | ||||
| # Remove a file, then wait for remove event | ||||
| (rmrf (string td1 "/file1.txt")) | ||||
| (expect :action :removed) | ||||
| (expect-empty) | ||||
|   # Check td2 | ||||
|   (spit-file td2 "file2.txt") | ||||
|   (expect :type :added) | ||||
|   (expect :type :modified) | ||||
|   (expect-empty) | ||||
|  | ||||
| # Unlisten to some events | ||||
| (filewatch/remove fw td2) | ||||
|   # Remove a file, then wait for remove event | ||||
|   (rmrf (string td1 "/file1.txt")) | ||||
|   (expect :type :removed) | ||||
|   (expect-empty) | ||||
|  | ||||
| # Check that we don't get anymore events from test directory 2 | ||||
| (spit-file td2 "file2.txt") | ||||
| (expect-empty) | ||||
|   # Unlisten to some events | ||||
|   (filewatch/remove fw td2) | ||||
|  | ||||
| # Repeat and things should still work with test directory 1 | ||||
| (spit-file td1 "file1.txt") | ||||
| (expect :action :added) | ||||
| (expect :action :modified) | ||||
| (expect-empty) | ||||
| (gccollect) | ||||
| (spit-file td1 "file1.txt") | ||||
| (expect :action :modified) | ||||
| (expect :action :modified) | ||||
| (expect-empty) | ||||
| (gccollect) | ||||
|   # Check that we don't get anymore events from test directory 2 | ||||
|   (spit-file td2 "file2.txt") | ||||
|   (expect-empty) | ||||
|  | ||||
|   # Repeat and things should still work with test directory 1 | ||||
|   (spit-file td1 "file1.txt") | ||||
|   (expect :type :added) | ||||
|   (expect :type :modified) | ||||
|   (expect-empty) | ||||
|   (gccollect) | ||||
|   (spit-file td1 "file1.txt") | ||||
|   (expect :type :modified) | ||||
|   (expect :type :modified) | ||||
|   (expect-empty) | ||||
|   (gccollect)) | ||||
|  | ||||
| # | ||||
| # Linux file writing | ||||
| # | ||||
|  | ||||
| (when is-linux | ||||
|   (spit-file td1 "file1.txt") | ||||
|   (expect :type :create) | ||||
|   (expect :type :close-write) | ||||
|   (expect-empty) | ||||
|   (gccollect) | ||||
|   (spit-file td1 "file1.txt") | ||||
|   (expect :type :close-write) | ||||
|   (expect-empty) | ||||
|   (gccollect) | ||||
|  | ||||
|   # Check td2 | ||||
|   (spit-file td2 "file2.txt") | ||||
|   (expect :type :create) | ||||
|   (expect :type :close-write) | ||||
|   (expect-empty) | ||||
|  | ||||
|   # Remove a file, then wait for remove event | ||||
|   (rmrf (string td1 "/file1.txt")) | ||||
|   (expect :type :delete) | ||||
|   (expect-empty) | ||||
|  | ||||
|   # Unlisten to some events | ||||
|   (filewatch/remove fw td2) | ||||
|   (expect :type :ignored) | ||||
|   (expect-empty) | ||||
|  | ||||
|   # Check that we don't get anymore events from test directory 2 | ||||
|   (spit-file td2 "file2.txt") | ||||
|   (expect-empty) | ||||
|  | ||||
|   # Repeat and things should still work with test directory 1 | ||||
|   (spit-file td1 "file1.txt") | ||||
|   (expect :type :create) | ||||
|   (expect :type :close-write) | ||||
|   (expect-empty) | ||||
|   (gccollect) | ||||
|   (spit-file td1 "file1.txt") | ||||
|   (expect :type :close-write) | ||||
|   (expect-empty) | ||||
|   (gccollect)) | ||||
|  | ||||
| (assert-no-error "filewatch/unlisten no error" (filewatch/unlisten fw)) | ||||
| (assert-no-error "cleanup 1" (rmrf td1)) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose