• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21 
22 #include "uv.h"
23 #include "task.h"
24 
25 #include <string.h>
26 #include <fcntl.h>
27 
28 #if defined(__APPLE__) && !TARGET_OS_IPHONE
29 # include <AvailabilityMacros.h>
30 #endif
31 
32 #ifndef HAVE_KQUEUE
33 # if defined(__APPLE__) ||                                                    \
34      defined(__DragonFly__) ||                                                \
35      defined(__FreeBSD__) ||                                                  \
36      defined(__FreeBSD_kernel__) ||                                           \
37      defined(__OpenBSD__) ||                                                  \
38      defined(__NetBSD__)
39 #  define HAVE_KQUEUE 1
40 # endif
41 #endif
42 
43 #if defined(__arm__)/* Increase the timeout so the test passes on arm CI bots */
44 # define CREATE_TIMEOUT 100
45 #else
46 # define CREATE_TIMEOUT 1
47 #endif
48 
49 static uv_fs_event_t fs_event;
50 static const char file_prefix[] = "fsevent-";
51 static const int fs_event_file_count = 16;
52 #if defined(__APPLE__) || defined(_WIN32)
53 static const char file_prefix_in_subdir[] = "subdir";
54 static int fs_multievent_cb_called;
55 #endif
56 static uv_timer_t timer;
57 static int timer_cb_called;
58 static int close_cb_called;
59 static int fs_event_created;
60 static int fs_event_removed;
61 static int fs_event_cb_called;
62 #if defined(PATH_MAX)
63 static char fs_event_filename[PATH_MAX];
64 #else
65 static char fs_event_filename[1024];
66 #endif  /* defined(PATH_MAX) */
67 static int timer_cb_touch_called;
68 static int timer_cb_exact_called;
69 
fs_event_fail(uv_fs_event_t * handle,const char * filename,int events,int status)70 static void fs_event_fail(uv_fs_event_t* handle,
71                           const char* filename,
72                           int events,
73                           int status) {
74   ASSERT(0 && "should never be called");
75 }
76 
create_dir(const char * name)77 static void create_dir(const char* name) {
78   int r;
79   uv_fs_t req;
80   r = uv_fs_mkdir(NULL, &req, name, 0755, NULL);
81   ASSERT(r == 0 || r == UV_EEXIST);
82   uv_fs_req_cleanup(&req);
83 }
84 
create_file(const char * name)85 static void create_file(const char* name) {
86   int r;
87   uv_file file;
88   uv_fs_t req;
89 
90   r = uv_fs_open(NULL, &req, name, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL);
91   ASSERT(r >= 0);
92   file = r;
93   uv_fs_req_cleanup(&req);
94   r = uv_fs_close(NULL, &req, file, NULL);
95   ASSERT(r == 0);
96   uv_fs_req_cleanup(&req);
97 }
98 
touch_file(const char * name)99 static void touch_file(const char* name) {
100   int r;
101   uv_file file;
102   uv_fs_t req;
103   uv_buf_t buf;
104 
105   r = uv_fs_open(NULL, &req, name, O_RDWR, 0, NULL);
106   ASSERT(r >= 0);
107   file = r;
108   uv_fs_req_cleanup(&req);
109 
110   buf = uv_buf_init("foo", 4);
111   r = uv_fs_write(NULL, &req, file, &buf, 1, -1, NULL);
112   ASSERT(r >= 0);
113   uv_fs_req_cleanup(&req);
114 
115   r = uv_fs_close(NULL, &req, file, NULL);
116   ASSERT(r == 0);
117   uv_fs_req_cleanup(&req);
118 }
119 
close_cb(uv_handle_t * handle)120 static void close_cb(uv_handle_t* handle) {
121   ASSERT_NOT_NULL(handle);
122   close_cb_called++;
123 }
124 
fail_cb(uv_fs_event_t * handle,const char * path,int events,int status)125 static void fail_cb(uv_fs_event_t* handle,
126                     const char* path,
127                     int events,
128                     int status) {
129   ASSERT(0 && "fail_cb called");
130 }
131 
fs_event_cb_dir(uv_fs_event_t * handle,const char * filename,int events,int status)132 static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename,
133   int events, int status) {
134   ++fs_event_cb_called;
135   ASSERT(handle == &fs_event);
136   ASSERT(status == 0);
137   ASSERT(events == UV_CHANGE);
138   #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
139   ASSERT(strcmp(filename, "file1") == 0);
140   #else
141   ASSERT(filename == NULL || strcmp(filename, "file1") == 0);
142   #endif
143   ASSERT(0 == uv_fs_event_stop(handle));
144   uv_close((uv_handle_t*)handle, close_cb);
145 }
146 
fs_event_get_filename(int i)147 static const char* fs_event_get_filename(int i) {
148   snprintf(fs_event_filename,
149            sizeof(fs_event_filename),
150            "watch_dir/%s%d",
151            file_prefix,
152            i);
153   return fs_event_filename;
154 }
155 
fs_event_create_files(uv_timer_t * handle)156 static void fs_event_create_files(uv_timer_t* handle) {
157   /* Make sure we're not attempting to create files we do not intend */
158   ASSERT(fs_event_created < fs_event_file_count);
159 
160   /* Create the file */
161   create_file(fs_event_get_filename(fs_event_created));
162 
163   if (++fs_event_created < fs_event_file_count) {
164     /* Create another file on a different event loop tick.  We do it this way
165      * to avoid fs events coalescing into one fs event. */
166     ASSERT(0 == uv_timer_start(&timer,
167                                fs_event_create_files,
168                                CREATE_TIMEOUT,
169                                0));
170   }
171 }
172 
fs_event_unlink_files(uv_timer_t * handle)173 static void fs_event_unlink_files(uv_timer_t* handle) {
174   int r;
175   int i;
176 
177   /* NOTE: handle might be NULL if invoked not as timer callback */
178   if (handle == NULL) {
179     /* Unlink all files */
180     for (i = 0; i < 16; i++) {
181       r = remove(fs_event_get_filename(i));
182       if (handle != NULL)
183         ASSERT(r == 0);
184     }
185   } else {
186     /* Make sure we're not attempting to remove files we do not intend */
187     ASSERT(fs_event_removed < fs_event_file_count);
188 
189     /* Remove the file */
190     ASSERT(0 == remove(fs_event_get_filename(fs_event_removed)));
191 
192     if (++fs_event_removed < fs_event_file_count) {
193       /* Remove another file on a different event loop tick.  We do it this way
194        * to avoid fs events coalescing into one fs event. */
195       ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files, 1, 0));
196     }
197   }
198 }
199 
fs_event_cb_dir_multi_file(uv_fs_event_t * handle,const char * filename,int events,int status)200 static void fs_event_cb_dir_multi_file(uv_fs_event_t* handle,
201                                        const char* filename,
202                                        int events,
203                                        int status) {
204   fs_event_cb_called++;
205   ASSERT(handle == &fs_event);
206   ASSERT(status == 0);
207   ASSERT(events == UV_CHANGE || events == UV_RENAME);
208   #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
209   ASSERT(strncmp(filename, file_prefix, sizeof(file_prefix) - 1) == 0);
210   #else
211   ASSERT(filename == NULL ||
212          strncmp(filename, file_prefix, sizeof(file_prefix) - 1) == 0);
213   #endif
214 
215   if (fs_event_created + fs_event_removed == fs_event_file_count) {
216     /* Once we've processed all create events, delete all files */
217     ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files, 1, 0));
218   } else if (fs_event_cb_called == 2 * fs_event_file_count) {
219     /* Once we've processed all create and delete events, stop watching */
220     uv_close((uv_handle_t*) &timer, close_cb);
221     uv_close((uv_handle_t*) handle, close_cb);
222   }
223 }
224 
225 #if defined(__APPLE__) || defined(_WIN32)
fs_event_get_filename_in_subdir(int i)226 static const char* fs_event_get_filename_in_subdir(int i) {
227   snprintf(fs_event_filename,
228            sizeof(fs_event_filename),
229            "watch_dir/subdir/%s%d",
230            file_prefix,
231            i);
232   return fs_event_filename;
233 }
234 
fs_event_create_files_in_subdir(uv_timer_t * handle)235 static void fs_event_create_files_in_subdir(uv_timer_t* handle) {
236   /* Make sure we're not attempting to create files we do not intend */
237   ASSERT(fs_event_created < fs_event_file_count);
238 
239   /* Create the file */
240   create_file(fs_event_get_filename_in_subdir(fs_event_created));
241 
242   if (++fs_event_created < fs_event_file_count) {
243     /* Create another file on a different event loop tick.  We do it this way
244      * to avoid fs events coalescing into one fs event. */
245     ASSERT(0 == uv_timer_start(&timer, fs_event_create_files_in_subdir, 1, 0));
246   }
247 }
248 
fs_event_unlink_files_in_subdir(uv_timer_t * handle)249 static void fs_event_unlink_files_in_subdir(uv_timer_t* handle) {
250   int r;
251   int i;
252 
253   /* NOTE: handle might be NULL if invoked not as timer callback */
254   if (handle == NULL) {
255     /* Unlink all files */
256     for (i = 0; i < 16; i++) {
257       r = remove(fs_event_get_filename_in_subdir(i));
258       if (handle != NULL)
259         ASSERT(r == 0);
260     }
261   } else {
262     /* Make sure we're not attempting to remove files we do not intend */
263     ASSERT(fs_event_removed < fs_event_file_count);
264 
265     /* Remove the file */
266     ASSERT(0 == remove(fs_event_get_filename_in_subdir(fs_event_removed)));
267 
268     if (++fs_event_removed < fs_event_file_count) {
269       /* Remove another file on a different event loop tick.  We do it this way
270        * to avoid fs events coalescing into one fs event. */
271       ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files_in_subdir, 1, 0));
272     }
273   }
274 }
275 
fs_event_cb_dir_multi_file_in_subdir(uv_fs_event_t * handle,const char * filename,int events,int status)276 static void fs_event_cb_dir_multi_file_in_subdir(uv_fs_event_t* handle,
277                                                  const char* filename,
278                                                  int events,
279                                                  int status) {
280 #ifdef _WIN32
281   /* Each file created (or deleted) will cause this callback to be called twice
282    * under Windows: once with the name of the file, and second time with the
283    * name of the directory. We will ignore the callback for the directory
284    * itself. */
285   if (filename && strcmp(filename, file_prefix_in_subdir) == 0)
286     return;
287 #endif
288   /* It may happen that the "subdir" creation event is captured even though
289    * we started watching after its actual creation.
290    */
291   if (strcmp(filename, "subdir") == 0)
292     return;
293 
294   fs_multievent_cb_called++;
295   ASSERT(handle == &fs_event);
296   ASSERT(status == 0);
297   ASSERT(events == UV_CHANGE || events == UV_RENAME);
298   #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
299   ASSERT(strncmp(filename,
300                  file_prefix_in_subdir,
301                  sizeof(file_prefix_in_subdir) - 1) == 0);
302   #else
303   ASSERT(filename == NULL ||
304          strncmp(filename,
305                  file_prefix_in_subdir,
306                  sizeof(file_prefix_in_subdir) - 1) == 0);
307   #endif
308 
309   if (fs_event_created == fs_event_file_count &&
310       fs_multievent_cb_called == fs_event_created) {
311     /* Once we've processed all create events, delete all files */
312     ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files_in_subdir, 1, 0));
313   } else if (fs_multievent_cb_called == 2 * fs_event_file_count) {
314     /* Once we've processed all create and delete events, stop watching */
315     ASSERT(fs_event_removed == fs_event_file_count);
316     uv_close((uv_handle_t*) &timer, close_cb);
317     uv_close((uv_handle_t*) handle, close_cb);
318   }
319 }
320 #endif
321 
fs_event_cb_file(uv_fs_event_t * handle,const char * filename,int events,int status)322 static void fs_event_cb_file(uv_fs_event_t* handle, const char* filename,
323   int events, int status) {
324   ++fs_event_cb_called;
325   ASSERT(handle == &fs_event);
326   ASSERT(status == 0);
327   ASSERT(events == UV_CHANGE);
328   #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
329   ASSERT(strcmp(filename, "file2") == 0);
330   #else
331   ASSERT(filename == NULL || strcmp(filename, "file2") == 0);
332   #endif
333   ASSERT(0 == uv_fs_event_stop(handle));
334   uv_close((uv_handle_t*)handle, close_cb);
335 }
336 
fs_event_cb_file_current_dir(uv_fs_event_t * handle,const char * filename,int events,int status)337 static void fs_event_cb_file_current_dir(uv_fs_event_t* handle,
338   const char* filename, int events, int status) {
339   ++fs_event_cb_called;
340 
341   ASSERT(handle == &fs_event);
342   ASSERT(status == 0);
343   ASSERT(events == UV_CHANGE);
344   #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
345   ASSERT(strcmp(filename, "watch_file") == 0);
346   #else
347   ASSERT(filename == NULL || strcmp(filename, "watch_file") == 0);
348   #endif
349 
350   uv_close((uv_handle_t*)handle, close_cb);
351 }
352 
timer_cb_file(uv_timer_t * handle)353 static void timer_cb_file(uv_timer_t* handle) {
354   ++timer_cb_called;
355 
356   if (timer_cb_called == 1) {
357     touch_file("watch_dir/file1");
358   } else {
359     touch_file("watch_dir/file2");
360     uv_close((uv_handle_t*)handle, close_cb);
361   }
362 }
363 
timer_cb_touch(uv_timer_t * timer)364 static void timer_cb_touch(uv_timer_t* timer) {
365   uv_close((uv_handle_t*)timer, NULL);
366   touch_file((char*) timer->data);
367   timer_cb_touch_called++;
368 }
369 
timer_cb_exact(uv_timer_t * handle)370 static void timer_cb_exact(uv_timer_t* handle) {
371   int r;
372 
373   if (timer_cb_exact_called == 0) {
374     touch_file("watch_dir/file.js");
375   } else {
376     uv_close((uv_handle_t*)handle, NULL);
377     r = uv_fs_event_stop(&fs_event);
378     ASSERT(r == 0);
379     uv_close((uv_handle_t*) &fs_event, NULL);
380   }
381 
382   ++timer_cb_exact_called;
383 }
384 
timer_cb_watch_twice(uv_timer_t * handle)385 static void timer_cb_watch_twice(uv_timer_t* handle) {
386   uv_fs_event_t* handles = handle->data;
387   uv_close((uv_handle_t*) (handles + 0), NULL);
388   uv_close((uv_handle_t*) (handles + 1), NULL);
389   uv_close((uv_handle_t*) handle, NULL);
390 }
391 
fs_event_cb_close(uv_fs_event_t * handle,const char * filename,int events,int status)392 static void fs_event_cb_close(uv_fs_event_t* handle,
393                               const char* filename,
394                               int events,
395                               int status) {
396   ASSERT(status == 0);
397 
398   ASSERT(fs_event_cb_called < 3);
399   ++fs_event_cb_called;
400 
401   if (fs_event_cb_called == 3) {
402     uv_close((uv_handle_t*) handle, close_cb);
403   }
404 }
405 
406 
TEST_IMPL(fs_event_watch_dir)407 TEST_IMPL(fs_event_watch_dir) {
408 #if defined(NO_FS_EVENTS)
409   RETURN_SKIP(NO_FS_EVENTS);
410 #elif defined(__MVS__)
411   RETURN_SKIP("Directory watching not supported on this platform.");
412 #endif
413 
414   uv_loop_t* loop = uv_default_loop();
415   int r;
416 
417   /* Setup */
418   fs_event_unlink_files(NULL);
419   remove("watch_dir/file2");
420   remove("watch_dir/file1");
421   remove("watch_dir/");
422   create_dir("watch_dir");
423 
424   r = uv_fs_event_init(loop, &fs_event);
425   ASSERT(r == 0);
426   r = uv_fs_event_start(&fs_event, fs_event_cb_dir_multi_file, "watch_dir", 0);
427   ASSERT(r == 0);
428   r = uv_timer_init(loop, &timer);
429   ASSERT(r == 0);
430   r = uv_timer_start(&timer, fs_event_create_files, 100, 0);
431   ASSERT(r == 0);
432 
433   uv_run(loop, UV_RUN_DEFAULT);
434 
435   ASSERT(fs_event_cb_called == fs_event_created + fs_event_removed);
436   ASSERT(close_cb_called == 2);
437 
438   /* Cleanup */
439   fs_event_unlink_files(NULL);
440   remove("watch_dir/file2");
441   remove("watch_dir/file1");
442   remove("watch_dir/");
443 
444   MAKE_VALGRIND_HAPPY();
445   return 0;
446 }
447 
448 
TEST_IMPL(fs_event_watch_dir_recursive)449 TEST_IMPL(fs_event_watch_dir_recursive) {
450 #if defined(__APPLE__) || defined(_WIN32)
451   uv_loop_t* loop;
452   int r;
453   uv_fs_event_t fs_event_root;
454 
455   /* Setup */
456   loop = uv_default_loop();
457   fs_event_unlink_files(NULL);
458   remove("watch_dir/file2");
459   remove("watch_dir/file1");
460   remove("watch_dir/subdir");
461   remove("watch_dir/");
462   create_dir("watch_dir");
463   create_dir("watch_dir/subdir");
464 
465   r = uv_fs_event_init(loop, &fs_event);
466   ASSERT(r == 0);
467   r = uv_fs_event_start(&fs_event,
468                         fs_event_cb_dir_multi_file_in_subdir,
469                         "watch_dir",
470                         UV_FS_EVENT_RECURSIVE);
471   ASSERT(r == 0);
472   r = uv_timer_init(loop, &timer);
473   ASSERT(r == 0);
474   r = uv_timer_start(&timer, fs_event_create_files_in_subdir, 100, 0);
475   ASSERT(r == 0);
476 
477 #ifndef _WIN32
478   /* Also try to watch the root directory.
479    * This will be noisier, so we're just checking for any couple events to happen. */
480   r = uv_fs_event_init(loop, &fs_event_root);
481   ASSERT(r == 0);
482   r = uv_fs_event_start(&fs_event_root,
483                         fs_event_cb_close,
484                         "/",
485                         UV_FS_EVENT_RECURSIVE);
486   ASSERT(r == 0);
487 #else
488   fs_event_cb_called += 3;
489   close_cb_called += 1;
490   (void)fs_event_root;
491 #endif
492 
493   uv_run(loop, UV_RUN_DEFAULT);
494 
495   ASSERT(fs_multievent_cb_called == fs_event_created + fs_event_removed);
496   ASSERT(fs_event_cb_called == 3);
497   ASSERT(close_cb_called == 3);
498 
499   /* Cleanup */
500   fs_event_unlink_files_in_subdir(NULL);
501   remove("watch_dir/file2");
502   remove("watch_dir/file1");
503   remove("watch_dir/subdir");
504   remove("watch_dir/");
505 
506   MAKE_VALGRIND_HAPPY();
507   return 0;
508 #else
509   RETURN_SKIP("Recursive directory watching not supported on this platform.");
510 #endif
511 }
512 
513 #ifdef _WIN32
TEST_IMPL(fs_event_watch_dir_short_path)514 TEST_IMPL(fs_event_watch_dir_short_path) {
515   uv_loop_t* loop;
516   uv_fs_t req;
517   int has_shortnames;
518   int r;
519 
520   /* Setup */
521   loop = uv_default_loop();
522   remove("watch_dir/file1");
523   remove("watch_dir/");
524   create_dir("watch_dir");
525   create_file("watch_dir/file1");
526 
527   /* Newer version of Windows ship with
528      HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\NtfsDisable8dot3NameCreation
529      not equal to 0. So we verify the files we created are addressable by a 8.3
530      short name */
531   has_shortnames = uv_fs_stat(NULL, &req, "watch_~1", NULL) != UV_ENOENT;
532   if (has_shortnames) {
533     r = uv_fs_event_init(loop, &fs_event);
534     ASSERT(r == 0);
535     r = uv_fs_event_start(&fs_event, fs_event_cb_dir, "watch_~1", 0);
536     ASSERT(r == 0);
537     r = uv_timer_init(loop, &timer);
538     ASSERT(r == 0);
539     r = uv_timer_start(&timer, timer_cb_file, 100, 0);
540     ASSERT(r == 0);
541 
542     uv_run(loop, UV_RUN_DEFAULT);
543 
544     ASSERT(fs_event_cb_called == 1);
545     ASSERT(timer_cb_called == 1);
546     ASSERT(close_cb_called == 1);
547   }
548 
549   /* Cleanup */
550   remove("watch_dir/file1");
551   remove("watch_dir/");
552 
553   MAKE_VALGRIND_HAPPY();
554 
555   if (!has_shortnames)
556     RETURN_SKIP("Was not able to address files with 8.3 short name.");
557 
558   return 0;
559 }
560 #endif
561 
562 
TEST_IMPL(fs_event_watch_file)563 TEST_IMPL(fs_event_watch_file) {
564 #if defined(NO_FS_EVENTS)
565   RETURN_SKIP(NO_FS_EVENTS);
566 #endif
567 
568   uv_loop_t* loop = uv_default_loop();
569   int r;
570 
571   /* Setup */
572   remove("watch_dir/file2");
573   remove("watch_dir/file1");
574   remove("watch_dir/");
575   create_dir("watch_dir");
576   create_file("watch_dir/file1");
577   create_file("watch_dir/file2");
578 
579   r = uv_fs_event_init(loop, &fs_event);
580   ASSERT(r == 0);
581   r = uv_fs_event_start(&fs_event, fs_event_cb_file, "watch_dir/file2", 0);
582   ASSERT(r == 0);
583   r = uv_timer_init(loop, &timer);
584   ASSERT(r == 0);
585   r = uv_timer_start(&timer, timer_cb_file, 100, 100);
586   ASSERT(r == 0);
587 
588   uv_run(loop, UV_RUN_DEFAULT);
589 
590   ASSERT(fs_event_cb_called == 1);
591   ASSERT(timer_cb_called == 2);
592   ASSERT(close_cb_called == 2);
593 
594   /* Cleanup */
595   remove("watch_dir/file2");
596   remove("watch_dir/file1");
597   remove("watch_dir/");
598 
599   MAKE_VALGRIND_HAPPY();
600   return 0;
601 }
602 
TEST_IMPL(fs_event_watch_file_exact_path)603 TEST_IMPL(fs_event_watch_file_exact_path) {
604   /*
605     This test watches a file named "file.jsx" and modifies a file named
606     "file.js". The test verifies that no events occur for file.jsx.
607   */
608 
609 #if defined(NO_FS_EVENTS)
610   RETURN_SKIP(NO_FS_EVENTS);
611 #endif
612 
613   uv_loop_t* loop;
614   int r;
615 
616   loop = uv_default_loop();
617 
618   /* Setup */
619   remove("watch_dir/file.js");
620   remove("watch_dir/file.jsx");
621   remove("watch_dir/");
622   create_dir("watch_dir");
623   create_file("watch_dir/file.js");
624   create_file("watch_dir/file.jsx");
625 #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_12)
626   /* Empirically, FSEvents seems to (reliably) report the preceeding
627    * create_file events prior to macOS 10.11.6 in the subsequent fs_watch
628    * creation, but that behavior hasn't been observed to occur on newer
629    * versions. Give a long delay here to let the system settle before running
630    * the test. */
631   uv_sleep(1100);
632   uv_update_time(loop);
633 #endif
634 
635   r = uv_fs_event_init(loop, &fs_event);
636   ASSERT(r == 0);
637   r = uv_fs_event_start(&fs_event, fs_event_fail, "watch_dir/file.jsx", 0);
638   ASSERT(r == 0);
639   r = uv_timer_init(loop, &timer);
640   ASSERT(r == 0);
641   r = uv_timer_start(&timer, timer_cb_exact, 100, 100);
642   ASSERT(r == 0);
643   r = uv_run(loop, UV_RUN_DEFAULT);
644   ASSERT(r == 0);
645   ASSERT(timer_cb_exact_called == 2);
646 
647   /* Cleanup */
648   remove("watch_dir/file.js");
649   remove("watch_dir/file.jsx");
650   remove("watch_dir/");
651 
652   MAKE_VALGRIND_HAPPY();
653   return 0;
654 }
655 
TEST_IMPL(fs_event_watch_file_twice)656 TEST_IMPL(fs_event_watch_file_twice) {
657 #if defined(NO_FS_EVENTS)
658   RETURN_SKIP(NO_FS_EVENTS);
659 #endif
660   const char path[] = "test/fixtures/empty_file";
661   uv_fs_event_t watchers[2];
662   uv_timer_t timer;
663   uv_loop_t* loop;
664 
665   loop = uv_default_loop();
666   timer.data = watchers;
667 
668   ASSERT(0 == uv_fs_event_init(loop, watchers + 0));
669   ASSERT(0 == uv_fs_event_start(watchers + 0, fail_cb, path, 0));
670   ASSERT(0 == uv_fs_event_init(loop, watchers + 1));
671   ASSERT(0 == uv_fs_event_start(watchers + 1, fail_cb, path, 0));
672   ASSERT(0 == uv_timer_init(loop, &timer));
673   ASSERT(0 == uv_timer_start(&timer, timer_cb_watch_twice, 10, 0));
674   ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT));
675 
676   MAKE_VALGRIND_HAPPY();
677   return 0;
678 }
679 
TEST_IMPL(fs_event_watch_file_current_dir)680 TEST_IMPL(fs_event_watch_file_current_dir) {
681 #if defined(NO_FS_EVENTS)
682   RETURN_SKIP(NO_FS_EVENTS);
683 #endif
684   uv_timer_t timer;
685   uv_loop_t* loop;
686   int r;
687 
688   loop = uv_default_loop();
689 
690   /* Setup */
691   remove("watch_file");
692   create_file("watch_file");
693 #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_12)
694   /* Empirically, kevent seems to (sometimes) report the preceeding
695    * create_file events prior to macOS 10.11.6 in the subsequent fs_event_start
696    * So let the system settle before running the test. */
697   uv_sleep(1100);
698   uv_update_time(loop);
699 #endif
700 
701   r = uv_fs_event_init(loop, &fs_event);
702   ASSERT(r == 0);
703   r = uv_fs_event_start(&fs_event,
704                         fs_event_cb_file_current_dir,
705                         "watch_file",
706                         0);
707   ASSERT(r == 0);
708 
709 
710   r = uv_timer_init(loop, &timer);
711   ASSERT(r == 0);
712 
713   timer.data = "watch_file";
714   r = uv_timer_start(&timer, timer_cb_touch, 1100, 0);
715   ASSERT(r == 0);
716 
717   ASSERT(timer_cb_touch_called == 0);
718   ASSERT(fs_event_cb_called == 0);
719   ASSERT(close_cb_called == 0);
720 
721   uv_run(loop, UV_RUN_DEFAULT);
722 
723   ASSERT(timer_cb_touch_called == 1);
724   /* FSEvents on macOS sometimes sends one change event, sometimes two. */
725   ASSERT_NE(0, fs_event_cb_called);
726   ASSERT(close_cb_called == 1);
727 
728   /* Cleanup */
729   remove("watch_file");
730 
731   MAKE_VALGRIND_HAPPY();
732   return 0;
733 }
734 
735 #ifdef _WIN32
TEST_IMPL(fs_event_watch_file_root_dir)736 TEST_IMPL(fs_event_watch_file_root_dir) {
737   uv_loop_t* loop;
738   int r;
739 
740   const char* sys_drive = getenv("SystemDrive");
741   char path[] = "\\\\?\\X:\\bootsect.bak";
742 
743   ASSERT_NOT_NULL(sys_drive);
744   strncpy(path + sizeof("\\\\?\\") - 1, sys_drive, 1);
745 
746   loop = uv_default_loop();
747 
748   r = uv_fs_event_init(loop, &fs_event);
749   ASSERT(r == 0);
750   r = uv_fs_event_start(&fs_event, fail_cb, path, 0);
751   if (r == UV_ENOENT)
752     RETURN_SKIP("bootsect.bak doesn't exist in system root.\n");
753   ASSERT(r == 0);
754 
755   uv_close((uv_handle_t*) &fs_event, NULL);
756 
757   MAKE_VALGRIND_HAPPY();
758   return 0;
759 }
760 #endif
761 
TEST_IMPL(fs_event_no_callback_after_close)762 TEST_IMPL(fs_event_no_callback_after_close) {
763 #if defined(NO_FS_EVENTS)
764   RETURN_SKIP(NO_FS_EVENTS);
765 #endif
766 
767   uv_loop_t* loop = uv_default_loop();
768   int r;
769 
770   /* Setup */
771   remove("watch_dir/file1");
772   remove("watch_dir/");
773   create_dir("watch_dir");
774   create_file("watch_dir/file1");
775 
776   r = uv_fs_event_init(loop, &fs_event);
777   ASSERT(r == 0);
778   r = uv_fs_event_start(&fs_event,
779                         fs_event_cb_file,
780                         "watch_dir/file1",
781                         0);
782   ASSERT(r == 0);
783 
784 
785   uv_close((uv_handle_t*)&fs_event, close_cb);
786   touch_file("watch_dir/file1");
787   uv_run(loop, UV_RUN_DEFAULT);
788 
789   ASSERT(fs_event_cb_called == 0);
790   ASSERT(close_cb_called == 1);
791 
792   /* Cleanup */
793   remove("watch_dir/file1");
794   remove("watch_dir/");
795 
796   MAKE_VALGRIND_HAPPY();
797   return 0;
798 }
799 
TEST_IMPL(fs_event_no_callback_on_close)800 TEST_IMPL(fs_event_no_callback_on_close) {
801 #if defined(NO_FS_EVENTS)
802   RETURN_SKIP(NO_FS_EVENTS);
803 #endif
804 
805   uv_loop_t* loop = uv_default_loop();
806   int r;
807 
808   /* Setup */
809   remove("watch_dir/file1");
810   remove("watch_dir/");
811   create_dir("watch_dir");
812   create_file("watch_dir/file1");
813 
814   r = uv_fs_event_init(loop, &fs_event);
815   ASSERT(r == 0);
816   r = uv_fs_event_start(&fs_event,
817                         fs_event_cb_file,
818                         "watch_dir/file1",
819                         0);
820   ASSERT(r == 0);
821 
822   uv_close((uv_handle_t*)&fs_event, close_cb);
823 
824   uv_run(loop, UV_RUN_DEFAULT);
825 
826   ASSERT(fs_event_cb_called == 0);
827   ASSERT(close_cb_called == 1);
828 
829   /* Cleanup */
830   remove("watch_dir/file1");
831   remove("watch_dir/");
832 
833   MAKE_VALGRIND_HAPPY();
834   return 0;
835 }
836 
837 
timer_cb(uv_timer_t * handle)838 static void timer_cb(uv_timer_t* handle) {
839   int r;
840 
841   r = uv_fs_event_init(handle->loop, &fs_event);
842   ASSERT(r == 0);
843   r = uv_fs_event_start(&fs_event, fs_event_fail, ".", 0);
844   ASSERT(r == 0);
845 
846   uv_close((uv_handle_t*)&fs_event, close_cb);
847   uv_close((uv_handle_t*)handle, close_cb);
848 }
849 
850 
TEST_IMPL(fs_event_immediate_close)851 TEST_IMPL(fs_event_immediate_close) {
852 #if defined(NO_FS_EVENTS)
853   RETURN_SKIP(NO_FS_EVENTS);
854 #endif
855   uv_timer_t timer;
856   uv_loop_t* loop;
857   int r;
858 
859   loop = uv_default_loop();
860 
861   r = uv_timer_init(loop, &timer);
862   ASSERT(r == 0);
863 
864   r = uv_timer_start(&timer, timer_cb, 1, 0);
865   ASSERT(r == 0);
866 
867   uv_run(loop, UV_RUN_DEFAULT);
868 
869   ASSERT(close_cb_called == 2);
870 
871   MAKE_VALGRIND_HAPPY();
872   return 0;
873 }
874 
875 
TEST_IMPL(fs_event_close_with_pending_event)876 TEST_IMPL(fs_event_close_with_pending_event) {
877 #if defined(NO_FS_EVENTS)
878   RETURN_SKIP(NO_FS_EVENTS);
879 #endif
880   uv_loop_t* loop;
881   int r;
882 
883   loop = uv_default_loop();
884 
885   create_dir("watch_dir");
886   create_file("watch_dir/file");
887 
888   r = uv_fs_event_init(loop, &fs_event);
889   ASSERT(r == 0);
890   r = uv_fs_event_start(&fs_event, fs_event_fail, "watch_dir", 0);
891   ASSERT(r == 0);
892 
893   /* Generate an fs event. */
894   touch_file("watch_dir/file");
895 
896   uv_close((uv_handle_t*)&fs_event, close_cb);
897 
898   uv_run(loop, UV_RUN_DEFAULT);
899 
900   ASSERT(close_cb_called == 1);
901 
902   /* Clean up */
903   remove("watch_dir/file");
904   remove("watch_dir/");
905 
906   MAKE_VALGRIND_HAPPY();
907   return 0;
908 }
909 
TEST_IMPL(fs_event_close_with_pending_delete_event)910 TEST_IMPL(fs_event_close_with_pending_delete_event) {
911 #if defined(NO_FS_EVENTS)
912   RETURN_SKIP(NO_FS_EVENTS);
913 #endif
914   uv_loop_t* loop;
915   int r;
916 
917   loop = uv_default_loop();
918 
919   create_dir("watch_dir");
920   create_file("watch_dir/file");
921 
922   r = uv_fs_event_init(loop, &fs_event);
923   ASSERT(r == 0);
924   r = uv_fs_event_start(&fs_event, fs_event_fail, "watch_dir/file", 0);
925   ASSERT(r == 0);
926 
927   /* Generate an fs event. */
928   remove("watch_dir/file");
929 
930   /* Allow time for the remove event to propagate to the pending list. */
931   /* XXX - perhaps just for __sun? */
932   uv_sleep(1100);
933   uv_update_time(loop);
934 
935   uv_close((uv_handle_t*)&fs_event, close_cb);
936 
937   uv_run(loop, UV_RUN_DEFAULT);
938 
939   ASSERT(close_cb_called == 1);
940 
941   /* Clean up */
942   remove("watch_dir/");
943 
944   MAKE_VALGRIND_HAPPY();
945   return 0;
946 }
947 
TEST_IMPL(fs_event_close_in_callback)948 TEST_IMPL(fs_event_close_in_callback) {
949 #if defined(NO_FS_EVENTS)
950   RETURN_SKIP(NO_FS_EVENTS);
951 #elif defined(__MVS__)
952   RETURN_SKIP("Directory watching not supported on this platform.");
953 #endif
954   uv_loop_t* loop;
955   int r;
956 
957   loop = uv_default_loop();
958 
959   fs_event_unlink_files(NULL);
960   create_dir("watch_dir");
961 
962   r = uv_fs_event_init(loop, &fs_event);
963   ASSERT(r == 0);
964   r = uv_fs_event_start(&fs_event, fs_event_cb_close, "watch_dir", 0);
965   ASSERT(r == 0);
966 
967   r = uv_timer_init(loop, &timer);
968   ASSERT(r == 0);
969   r = uv_timer_start(&timer, fs_event_create_files, 100, 0);
970   ASSERT(r == 0);
971 
972   uv_run(loop, UV_RUN_DEFAULT);
973 
974   uv_close((uv_handle_t*)&timer, close_cb);
975 
976   uv_run(loop, UV_RUN_ONCE);
977 
978   ASSERT(close_cb_called == 2);
979   ASSERT(fs_event_cb_called == 3);
980 
981   /* Clean up */
982   fs_event_unlink_files(NULL);
983   remove("watch_dir/");
984 
985   MAKE_VALGRIND_HAPPY();
986   return 0;
987 }
988 
TEST_IMPL(fs_event_start_and_close)989 TEST_IMPL(fs_event_start_and_close) {
990 #if defined(NO_FS_EVENTS)
991   RETURN_SKIP(NO_FS_EVENTS);
992 #endif
993   uv_loop_t* loop;
994   uv_fs_event_t fs_event1;
995   uv_fs_event_t fs_event2;
996   int r;
997 
998   loop = uv_default_loop();
999 
1000   create_dir("watch_dir");
1001 
1002   r = uv_fs_event_init(loop, &fs_event1);
1003   ASSERT(r == 0);
1004   r = uv_fs_event_start(&fs_event1, fs_event_cb_dir, "watch_dir", 0);
1005   ASSERT(r == 0);
1006 
1007   r = uv_fs_event_init(loop, &fs_event2);
1008   ASSERT(r == 0);
1009   r = uv_fs_event_start(&fs_event2, fs_event_cb_dir, "watch_dir", 0);
1010   ASSERT(r == 0);
1011 
1012   uv_close((uv_handle_t*) &fs_event2, close_cb);
1013   uv_close((uv_handle_t*) &fs_event1, close_cb);
1014 
1015   uv_run(loop, UV_RUN_DEFAULT);
1016 
1017   ASSERT(close_cb_called == 2);
1018 
1019   remove("watch_dir/");
1020   MAKE_VALGRIND_HAPPY();
1021   return 0;
1022 }
1023 
TEST_IMPL(fs_event_getpath)1024 TEST_IMPL(fs_event_getpath) {
1025 #if defined(NO_FS_EVENTS)
1026   RETURN_SKIP(NO_FS_EVENTS);
1027 #endif
1028   uv_loop_t* loop = uv_default_loop();
1029   unsigned i;
1030   int r;
1031   char buf[1024];
1032   size_t len;
1033   const char* const watch_dir[] = {
1034     "watch_dir",
1035     "watch_dir/",
1036     "watch_dir///",
1037     "watch_dir/subfolder/..",
1038     "watch_dir//subfolder//..//",
1039   };
1040 
1041   create_dir("watch_dir");
1042   create_dir("watch_dir/subfolder");
1043 
1044 
1045   for (i = 0; i < ARRAY_SIZE(watch_dir); i++) {
1046     r = uv_fs_event_init(loop, &fs_event);
1047     ASSERT(r == 0);
1048     len = sizeof buf;
1049     r = uv_fs_event_getpath(&fs_event, buf, &len);
1050     ASSERT(r == UV_EINVAL);
1051     r = uv_fs_event_start(&fs_event, fail_cb, watch_dir[i], 0);
1052     ASSERT(r == 0);
1053     len = 0;
1054     r = uv_fs_event_getpath(&fs_event, buf, &len);
1055     ASSERT(r == UV_ENOBUFS);
1056     ASSERT(len < sizeof buf); /* sanity check */
1057     ASSERT(len == strlen(watch_dir[i]) + 1);
1058     r = uv_fs_event_getpath(&fs_event, buf, &len);
1059     ASSERT(r == 0);
1060     ASSERT(len == strlen(watch_dir[i]));
1061     ASSERT(strcmp(buf, watch_dir[i]) == 0);
1062     r = uv_fs_event_stop(&fs_event);
1063     ASSERT(r == 0);
1064     uv_close((uv_handle_t*) &fs_event, close_cb);
1065 
1066     uv_run(loop, UV_RUN_DEFAULT);
1067 
1068     ASSERT(close_cb_called == 1);
1069     close_cb_called = 0;
1070   }
1071 
1072   remove("watch_dir/");
1073   MAKE_VALGRIND_HAPPY();
1074   return 0;
1075 }
1076 
1077 #if defined(__APPLE__)
1078 
1079 static int fs_event_error_reported;
1080 
fs_event_error_report_cb(uv_fs_event_t * handle,const char * filename,int events,int status)1081 static void fs_event_error_report_cb(uv_fs_event_t* handle,
1082                                      const char* filename,
1083                                      int events,
1084                                      int status) {
1085   if (status != 0)
1086     fs_event_error_reported = status;
1087 }
1088 
timer_cb_nop(uv_timer_t * handle)1089 static void timer_cb_nop(uv_timer_t* handle) {
1090   ++timer_cb_called;
1091   uv_close((uv_handle_t*) handle, close_cb);
1092 }
1093 
fs_event_error_report_close_cb(uv_handle_t * handle)1094 static void fs_event_error_report_close_cb(uv_handle_t* handle) {
1095   ASSERT_NOT_NULL(handle);
1096   close_cb_called++;
1097 
1098   /* handle is allocated on-stack, no need to free it */
1099 }
1100 
1101 
TEST_IMPL(fs_event_error_reporting)1102 TEST_IMPL(fs_event_error_reporting) {
1103   unsigned int i;
1104   uv_loop_t loops[1024];
1105   uv_fs_event_t events[ARRAY_SIZE(loops)];
1106   uv_loop_t* loop;
1107   uv_fs_event_t* event;
1108 
1109   TEST_FILE_LIMIT(ARRAY_SIZE(loops) * 3);
1110 
1111   remove("watch_dir/");
1112   create_dir("watch_dir");
1113 
1114   /* Create a lot of loops, and start FSEventStream in each of them.
1115    * Eventually, this should create enough streams to make FSEventStreamStart()
1116    * fail.
1117    */
1118   for (i = 0; i < ARRAY_SIZE(loops); i++) {
1119     loop = &loops[i];
1120     ASSERT(0 == uv_loop_init(loop));
1121     event = &events[i];
1122 
1123     timer_cb_called = 0;
1124     close_cb_called = 0;
1125     ASSERT(0 == uv_fs_event_init(loop, event));
1126     ASSERT(0 == uv_fs_event_start(event,
1127                                   fs_event_error_report_cb,
1128                                   "watch_dir",
1129                                   0));
1130     uv_unref((uv_handle_t*) event);
1131 
1132     /* Let loop run for some time */
1133     ASSERT(0 == uv_timer_init(loop, &timer));
1134     ASSERT(0 == uv_timer_start(&timer, timer_cb_nop, 2, 0));
1135     uv_run(loop, UV_RUN_DEFAULT);
1136     ASSERT(1 == timer_cb_called);
1137     ASSERT(1 == close_cb_called);
1138     if (fs_event_error_reported != 0)
1139       break;
1140   }
1141 
1142   /* At least one loop should fail */
1143   ASSERT(fs_event_error_reported == UV_EMFILE);
1144 
1145   /* Stop and close all events, and destroy loops */
1146   do {
1147     loop = &loops[i];
1148     event = &events[i];
1149 
1150     ASSERT(0 == uv_fs_event_stop(event));
1151     uv_ref((uv_handle_t*) event);
1152     uv_close((uv_handle_t*) event, fs_event_error_report_close_cb);
1153 
1154     close_cb_called = 0;
1155     uv_run(loop, UV_RUN_DEFAULT);
1156     ASSERT(close_cb_called == 1);
1157 
1158     uv_loop_close(loop);
1159   } while (i-- != 0);
1160 
1161   remove("watch_dir/");
1162   MAKE_VALGRIND_HAPPY();
1163   return 0;
1164 }
1165 
1166 #else  /* !defined(__APPLE__) */
1167 
TEST_IMPL(fs_event_error_reporting)1168 TEST_IMPL(fs_event_error_reporting) {
1169   /* No-op, needed only for FSEvents backend */
1170 
1171   MAKE_VALGRIND_HAPPY();
1172   return 0;
1173 }
1174 
1175 #endif  /* defined(__APPLE__) */
1176 
TEST_IMPL(fs_event_watch_invalid_path)1177 TEST_IMPL(fs_event_watch_invalid_path) {
1178 #if defined(NO_FS_EVENTS)
1179   RETURN_SKIP(NO_FS_EVENTS);
1180 #endif
1181 
1182   uv_loop_t* loop;
1183   int r;
1184 
1185   loop = uv_default_loop();
1186   r = uv_fs_event_init(loop, &fs_event);
1187   ASSERT(r == 0);
1188   r = uv_fs_event_start(&fs_event, fs_event_cb_file, "<:;", 0);
1189   ASSERT(r != 0);
1190   ASSERT(uv_is_active((uv_handle_t*) &fs_event) == 0);
1191   r = uv_fs_event_start(&fs_event, fs_event_cb_file, "", 0);
1192   ASSERT(r != 0);
1193   ASSERT(uv_is_active((uv_handle_t*) &fs_event) == 0);
1194   MAKE_VALGRIND_HAPPY();
1195   return 0;
1196 }
1197 
1198 static int fs_event_cb_stop_calls;
1199 
fs_event_cb_stop(uv_fs_event_t * handle,const char * path,int events,int status)1200 static void fs_event_cb_stop(uv_fs_event_t* handle, const char* path,
1201                              int events, int status) {
1202   uv_fs_event_stop(handle);
1203   fs_event_cb_stop_calls++;
1204 }
1205 
TEST_IMPL(fs_event_stop_in_cb)1206 TEST_IMPL(fs_event_stop_in_cb) {
1207   uv_fs_event_t fs;
1208   uv_timer_t timer;
1209   char path[] = "fs_event_stop_in_cb.txt";
1210 
1211 #if defined(NO_FS_EVENTS)
1212   RETURN_SKIP(NO_FS_EVENTS);
1213 #endif
1214 
1215   remove(path);
1216   create_file(path);
1217 
1218   ASSERT_EQ(0, uv_fs_event_init(uv_default_loop(), &fs));
1219   ASSERT_EQ(0, uv_fs_event_start(&fs, fs_event_cb_stop, path, 0));
1220 
1221   /* Note: timer_cb_touch() closes the handle. */
1222   timer.data = path;
1223   ASSERT_EQ(0, uv_timer_init(uv_default_loop(), &timer));
1224   ASSERT_EQ(0, uv_timer_start(&timer, timer_cb_touch, 100, 0));
1225 
1226   ASSERT_EQ(0, fs_event_cb_stop_calls);
1227   ASSERT_EQ(0, timer_cb_touch_called);
1228 
1229   ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT));
1230 
1231   ASSERT_EQ(1, fs_event_cb_stop_calls);
1232   ASSERT_EQ(1, timer_cb_touch_called);
1233 
1234   uv_close((uv_handle_t*) &fs, NULL);
1235   ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT));
1236   ASSERT_EQ(1, fs_event_cb_stop_calls);
1237 
1238   remove(path);
1239 
1240   MAKE_VALGRIND_HAPPY();
1241   return 0;
1242 }
1243