• 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 
timer_cb_close_handle(uv_timer_t * timer)337 static void timer_cb_close_handle(uv_timer_t* timer) {
338   uv_handle_t* handle;
339 
340   ASSERT_NOT_NULL(timer);
341   handle = timer->data;
342 
343   uv_close((uv_handle_t*)timer, NULL);
344   uv_close((uv_handle_t*)handle, close_cb);
345 }
346 
fs_event_cb_file_current_dir(uv_fs_event_t * handle,const char * filename,int events,int status)347 static void fs_event_cb_file_current_dir(uv_fs_event_t* handle,
348   const char* filename, int events, int status) {
349   ASSERT(fs_event_cb_called == 0);
350   ++fs_event_cb_called;
351 
352   ASSERT(handle == &fs_event);
353   ASSERT(status == 0);
354   ASSERT(events == UV_CHANGE);
355   #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
356   ASSERT(strcmp(filename, "watch_file") == 0);
357   #else
358   ASSERT(filename == NULL || strcmp(filename, "watch_file") == 0);
359   #endif
360 
361   /* Regression test for SunOS: touch should generate just one event. */
362   {
363     static uv_timer_t timer;
364     uv_timer_init(handle->loop, &timer);
365     timer.data = handle;
366     uv_timer_start(&timer, timer_cb_close_handle, 250, 0);
367   }
368 }
369 
timer_cb_file(uv_timer_t * handle)370 static void timer_cb_file(uv_timer_t* handle) {
371   ++timer_cb_called;
372 
373   if (timer_cb_called == 1) {
374     touch_file("watch_dir/file1");
375   } else {
376     touch_file("watch_dir/file2");
377     uv_close((uv_handle_t*)handle, close_cb);
378   }
379 }
380 
timer_cb_touch(uv_timer_t * timer)381 static void timer_cb_touch(uv_timer_t* timer) {
382   uv_close((uv_handle_t*)timer, NULL);
383   touch_file((char*) timer->data);
384   timer_cb_touch_called++;
385 }
386 
timer_cb_exact(uv_timer_t * handle)387 static void timer_cb_exact(uv_timer_t* handle) {
388   int r;
389 
390   if (timer_cb_exact_called == 0) {
391     touch_file("watch_dir/file.js");
392   } else {
393     uv_close((uv_handle_t*)handle, NULL);
394     r = uv_fs_event_stop(&fs_event);
395     ASSERT(r == 0);
396     uv_close((uv_handle_t*) &fs_event, NULL);
397   }
398 
399   ++timer_cb_exact_called;
400 }
401 
timer_cb_watch_twice(uv_timer_t * handle)402 static void timer_cb_watch_twice(uv_timer_t* handle) {
403   uv_fs_event_t* handles = handle->data;
404   uv_close((uv_handle_t*) (handles + 0), NULL);
405   uv_close((uv_handle_t*) (handles + 1), NULL);
406   uv_close((uv_handle_t*) handle, NULL);
407 }
408 
fs_event_cb_close(uv_fs_event_t * handle,const char * filename,int events,int status)409 static void fs_event_cb_close(uv_fs_event_t* handle,
410                               const char* filename,
411                               int events,
412                               int status) {
413   ASSERT(status == 0);
414 
415   ASSERT(fs_event_cb_called < 3);
416   ++fs_event_cb_called;
417 
418   if (fs_event_cb_called == 3) {
419     uv_close((uv_handle_t*) handle, close_cb);
420   }
421 }
422 
423 
TEST_IMPL(fs_event_watch_dir)424 TEST_IMPL(fs_event_watch_dir) {
425 #if defined(NO_FS_EVENTS)
426   RETURN_SKIP(NO_FS_EVENTS);
427 #elif defined(__MVS__)
428   RETURN_SKIP("Directory watching not supported on this platform.");
429 #endif
430 
431   uv_loop_t* loop = uv_default_loop();
432   int r;
433 
434   /* Setup */
435   fs_event_unlink_files(NULL);
436   remove("watch_dir/file2");
437   remove("watch_dir/file1");
438   remove("watch_dir/");
439   create_dir("watch_dir");
440 
441   r = uv_fs_event_init(loop, &fs_event);
442   ASSERT(r == 0);
443   r = uv_fs_event_start(&fs_event, fs_event_cb_dir_multi_file, "watch_dir", 0);
444   ASSERT(r == 0);
445   r = uv_timer_init(loop, &timer);
446   ASSERT(r == 0);
447   r = uv_timer_start(&timer, fs_event_create_files, 100, 0);
448   ASSERT(r == 0);
449 
450   uv_run(loop, UV_RUN_DEFAULT);
451 
452   ASSERT(fs_event_cb_called == fs_event_created + fs_event_removed);
453   ASSERT(close_cb_called == 2);
454 
455   /* Cleanup */
456   fs_event_unlink_files(NULL);
457   remove("watch_dir/file2");
458   remove("watch_dir/file1");
459   remove("watch_dir/");
460 
461   MAKE_VALGRIND_HAPPY();
462   return 0;
463 }
464 
465 
TEST_IMPL(fs_event_watch_dir_recursive)466 TEST_IMPL(fs_event_watch_dir_recursive) {
467 #if defined(__APPLE__) || defined(_WIN32)
468   uv_loop_t* loop;
469   int r;
470   uv_fs_event_t fs_event_root;
471 
472   /* Setup */
473   loop = uv_default_loop();
474   fs_event_unlink_files(NULL);
475   remove("watch_dir/file2");
476   remove("watch_dir/file1");
477   remove("watch_dir/subdir");
478   remove("watch_dir/");
479   create_dir("watch_dir");
480   create_dir("watch_dir/subdir");
481 
482   r = uv_fs_event_init(loop, &fs_event);
483   ASSERT(r == 0);
484   r = uv_fs_event_start(&fs_event,
485                         fs_event_cb_dir_multi_file_in_subdir,
486                         "watch_dir",
487                         UV_FS_EVENT_RECURSIVE);
488   ASSERT(r == 0);
489   r = uv_timer_init(loop, &timer);
490   ASSERT(r == 0);
491   r = uv_timer_start(&timer, fs_event_create_files_in_subdir, 100, 0);
492   ASSERT(r == 0);
493 
494 #ifndef _WIN32
495   /* Also try to watch the root directory.
496    * This will be noisier, so we're just checking for any couple events to happen. */
497   r = uv_fs_event_init(loop, &fs_event_root);
498   ASSERT(r == 0);
499   r = uv_fs_event_start(&fs_event_root,
500                         fs_event_cb_close,
501                         "/",
502                         UV_FS_EVENT_RECURSIVE);
503   ASSERT(r == 0);
504 #else
505   fs_event_cb_called += 3;
506   close_cb_called += 1;
507   (void)fs_event_root;
508 #endif
509 
510   uv_run(loop, UV_RUN_DEFAULT);
511 
512   ASSERT(fs_multievent_cb_called == fs_event_created + fs_event_removed);
513   ASSERT(fs_event_cb_called == 3);
514   ASSERT(close_cb_called == 3);
515 
516   /* Cleanup */
517   fs_event_unlink_files_in_subdir(NULL);
518   remove("watch_dir/file2");
519   remove("watch_dir/file1");
520   remove("watch_dir/subdir");
521   remove("watch_dir/");
522 
523   MAKE_VALGRIND_HAPPY();
524   return 0;
525 #else
526   RETURN_SKIP("Recursive directory watching not supported on this platform.");
527 #endif
528 }
529 
530 #ifdef _WIN32
TEST_IMPL(fs_event_watch_dir_short_path)531 TEST_IMPL(fs_event_watch_dir_short_path) {
532   uv_loop_t* loop;
533   uv_fs_t req;
534   int has_shortnames;
535   int r;
536 
537   /* Setup */
538   loop = uv_default_loop();
539   remove("watch_dir/file1");
540   remove("watch_dir/");
541   create_dir("watch_dir");
542   create_file("watch_dir/file1");
543 
544   /* Newer version of Windows ship with
545      HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\NtfsDisable8dot3NameCreation
546      not equal to 0. So we verify the files we created are addressable by a 8.3
547      short name */
548   has_shortnames = uv_fs_stat(NULL, &req, "watch_~1", NULL) != UV_ENOENT;
549   if (has_shortnames) {
550     r = uv_fs_event_init(loop, &fs_event);
551     ASSERT(r == 0);
552     r = uv_fs_event_start(&fs_event, fs_event_cb_dir, "watch_~1", 0);
553     ASSERT(r == 0);
554     r = uv_timer_init(loop, &timer);
555     ASSERT(r == 0);
556     r = uv_timer_start(&timer, timer_cb_file, 100, 0);
557     ASSERT(r == 0);
558 
559     uv_run(loop, UV_RUN_DEFAULT);
560 
561     ASSERT(fs_event_cb_called == 1);
562     ASSERT(timer_cb_called == 1);
563     ASSERT(close_cb_called == 1);
564   }
565 
566   /* Cleanup */
567   remove("watch_dir/file1");
568   remove("watch_dir/");
569 
570   MAKE_VALGRIND_HAPPY();
571 
572   if (!has_shortnames)
573     RETURN_SKIP("Was not able to address files with 8.3 short name.");
574 
575   return 0;
576 }
577 #endif
578 
579 
TEST_IMPL(fs_event_watch_file)580 TEST_IMPL(fs_event_watch_file) {
581 #if defined(NO_FS_EVENTS)
582   RETURN_SKIP(NO_FS_EVENTS);
583 #endif
584 
585   uv_loop_t* loop = uv_default_loop();
586   int r;
587 
588   /* Setup */
589   remove("watch_dir/file2");
590   remove("watch_dir/file1");
591   remove("watch_dir/");
592   create_dir("watch_dir");
593   create_file("watch_dir/file1");
594   create_file("watch_dir/file2");
595 
596   r = uv_fs_event_init(loop, &fs_event);
597   ASSERT(r == 0);
598   r = uv_fs_event_start(&fs_event, fs_event_cb_file, "watch_dir/file2", 0);
599   ASSERT(r == 0);
600   r = uv_timer_init(loop, &timer);
601   ASSERT(r == 0);
602   r = uv_timer_start(&timer, timer_cb_file, 100, 100);
603   ASSERT(r == 0);
604 
605   uv_run(loop, UV_RUN_DEFAULT);
606 
607   ASSERT(fs_event_cb_called == 1);
608   ASSERT(timer_cb_called == 2);
609   ASSERT(close_cb_called == 2);
610 
611   /* Cleanup */
612   remove("watch_dir/file2");
613   remove("watch_dir/file1");
614   remove("watch_dir/");
615 
616   MAKE_VALGRIND_HAPPY();
617   return 0;
618 }
619 
TEST_IMPL(fs_event_watch_file_exact_path)620 TEST_IMPL(fs_event_watch_file_exact_path) {
621   /*
622     This test watches a file named "file.jsx" and modifies a file named
623     "file.js". The test verifies that no events occur for file.jsx.
624   */
625 
626 #if defined(NO_FS_EVENTS)
627   RETURN_SKIP(NO_FS_EVENTS);
628 #endif
629 
630   uv_loop_t* loop;
631   int r;
632 
633   loop = uv_default_loop();
634 
635   /* Setup */
636   remove("watch_dir/file.js");
637   remove("watch_dir/file.jsx");
638   remove("watch_dir/");
639   create_dir("watch_dir");
640   create_file("watch_dir/file.js");
641   create_file("watch_dir/file.jsx");
642 #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_12)
643   /* Empirically, FSEvents seems to (reliably) report the preceeding
644    * create_file events prior to macOS 10.11.6 in the subsequent fs_watch
645    * creation, but that behavior hasn't been observed to occur on newer
646    * versions. Give a long delay here to let the system settle before running
647    * the test. */
648   uv_sleep(1100);
649   uv_update_time(loop);
650 #endif
651 
652   r = uv_fs_event_init(loop, &fs_event);
653   ASSERT(r == 0);
654   r = uv_fs_event_start(&fs_event, fs_event_fail, "watch_dir/file.jsx", 0);
655   ASSERT(r == 0);
656   r = uv_timer_init(loop, &timer);
657   ASSERT(r == 0);
658   r = uv_timer_start(&timer, timer_cb_exact, 100, 100);
659   ASSERT(r == 0);
660   r = uv_run(loop, UV_RUN_DEFAULT);
661   ASSERT(r == 0);
662   ASSERT(timer_cb_exact_called == 2);
663 
664   /* Cleanup */
665   remove("watch_dir/file.js");
666   remove("watch_dir/file.jsx");
667   remove("watch_dir/");
668 
669   MAKE_VALGRIND_HAPPY();
670   return 0;
671 }
672 
TEST_IMPL(fs_event_watch_file_twice)673 TEST_IMPL(fs_event_watch_file_twice) {
674 #if defined(NO_FS_EVENTS)
675   RETURN_SKIP(NO_FS_EVENTS);
676 #endif
677   const char path[] = "test/fixtures/empty_file";
678   uv_fs_event_t watchers[2];
679   uv_timer_t timer;
680   uv_loop_t* loop;
681 
682   loop = uv_default_loop();
683   timer.data = watchers;
684 
685   ASSERT(0 == uv_fs_event_init(loop, watchers + 0));
686   ASSERT(0 == uv_fs_event_start(watchers + 0, fail_cb, path, 0));
687   ASSERT(0 == uv_fs_event_init(loop, watchers + 1));
688   ASSERT(0 == uv_fs_event_start(watchers + 1, fail_cb, path, 0));
689   ASSERT(0 == uv_timer_init(loop, &timer));
690   ASSERT(0 == uv_timer_start(&timer, timer_cb_watch_twice, 10, 0));
691   ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT));
692 
693   MAKE_VALGRIND_HAPPY();
694   return 0;
695 }
696 
TEST_IMPL(fs_event_watch_file_current_dir)697 TEST_IMPL(fs_event_watch_file_current_dir) {
698 #if defined(NO_FS_EVENTS)
699   RETURN_SKIP(NO_FS_EVENTS);
700 #endif
701   uv_timer_t timer;
702   uv_loop_t* loop;
703   int r;
704 
705   loop = uv_default_loop();
706 
707   /* Setup */
708   remove("watch_file");
709   create_file("watch_file");
710 #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_12)
711   /* Empirically, kevent seems to (sometimes) report the preceeding
712    * create_file events prior to macOS 10.11.6 in the subsequent fs_event_start
713    * So let the system settle before running the test. */
714   uv_sleep(1100);
715   uv_update_time(loop);
716 #endif
717 
718   r = uv_fs_event_init(loop, &fs_event);
719   ASSERT(r == 0);
720   r = uv_fs_event_start(&fs_event,
721                         fs_event_cb_file_current_dir,
722                         "watch_file",
723                         0);
724   ASSERT(r == 0);
725 
726 
727   r = uv_timer_init(loop, &timer);
728   ASSERT(r == 0);
729 
730   timer.data = "watch_file";
731   r = uv_timer_start(&timer, timer_cb_touch, 1100, 0);
732   ASSERT(r == 0);
733 
734   ASSERT(timer_cb_touch_called == 0);
735   ASSERT(fs_event_cb_called == 0);
736   ASSERT(close_cb_called == 0);
737 
738   uv_run(loop, UV_RUN_DEFAULT);
739 
740   ASSERT(timer_cb_touch_called == 1);
741   ASSERT(fs_event_cb_called == 1);
742   ASSERT(close_cb_called == 1);
743 
744   /* Cleanup */
745   remove("watch_file");
746 
747   MAKE_VALGRIND_HAPPY();
748   return 0;
749 }
750 
751 #ifdef _WIN32
TEST_IMPL(fs_event_watch_file_root_dir)752 TEST_IMPL(fs_event_watch_file_root_dir) {
753   uv_loop_t* loop;
754   int r;
755 
756   const char* sys_drive = getenv("SystemDrive");
757   char path[] = "\\\\?\\X:\\bootsect.bak";
758 
759   ASSERT_NOT_NULL(sys_drive);
760   strncpy(path + sizeof("\\\\?\\") - 1, sys_drive, 1);
761 
762   loop = uv_default_loop();
763 
764   r = uv_fs_event_init(loop, &fs_event);
765   ASSERT(r == 0);
766   r = uv_fs_event_start(&fs_event, fail_cb, path, 0);
767   if (r == UV_ENOENT)
768     RETURN_SKIP("bootsect.bak doesn't exist in system root.\n");
769   ASSERT(r == 0);
770 
771   uv_close((uv_handle_t*) &fs_event, NULL);
772 
773   MAKE_VALGRIND_HAPPY();
774   return 0;
775 }
776 #endif
777 
TEST_IMPL(fs_event_no_callback_after_close)778 TEST_IMPL(fs_event_no_callback_after_close) {
779 #if defined(NO_FS_EVENTS)
780   RETURN_SKIP(NO_FS_EVENTS);
781 #endif
782 
783   uv_loop_t* loop = uv_default_loop();
784   int r;
785 
786   /* Setup */
787   remove("watch_dir/file1");
788   remove("watch_dir/");
789   create_dir("watch_dir");
790   create_file("watch_dir/file1");
791 
792   r = uv_fs_event_init(loop, &fs_event);
793   ASSERT(r == 0);
794   r = uv_fs_event_start(&fs_event,
795                         fs_event_cb_file,
796                         "watch_dir/file1",
797                         0);
798   ASSERT(r == 0);
799 
800 
801   uv_close((uv_handle_t*)&fs_event, close_cb);
802   touch_file("watch_dir/file1");
803   uv_run(loop, UV_RUN_DEFAULT);
804 
805   ASSERT(fs_event_cb_called == 0);
806   ASSERT(close_cb_called == 1);
807 
808   /* Cleanup */
809   remove("watch_dir/file1");
810   remove("watch_dir/");
811 
812   MAKE_VALGRIND_HAPPY();
813   return 0;
814 }
815 
TEST_IMPL(fs_event_no_callback_on_close)816 TEST_IMPL(fs_event_no_callback_on_close) {
817 #if defined(NO_FS_EVENTS)
818   RETURN_SKIP(NO_FS_EVENTS);
819 #endif
820 
821   uv_loop_t* loop = uv_default_loop();
822   int r;
823 
824   /* Setup */
825   remove("watch_dir/file1");
826   remove("watch_dir/");
827   create_dir("watch_dir");
828   create_file("watch_dir/file1");
829 
830   r = uv_fs_event_init(loop, &fs_event);
831   ASSERT(r == 0);
832   r = uv_fs_event_start(&fs_event,
833                         fs_event_cb_file,
834                         "watch_dir/file1",
835                         0);
836   ASSERT(r == 0);
837 
838   uv_close((uv_handle_t*)&fs_event, close_cb);
839 
840   uv_run(loop, UV_RUN_DEFAULT);
841 
842   ASSERT(fs_event_cb_called == 0);
843   ASSERT(close_cb_called == 1);
844 
845   /* Cleanup */
846   remove("watch_dir/file1");
847   remove("watch_dir/");
848 
849   MAKE_VALGRIND_HAPPY();
850   return 0;
851 }
852 
853 
timer_cb(uv_timer_t * handle)854 static void timer_cb(uv_timer_t* handle) {
855   int r;
856 
857   r = uv_fs_event_init(handle->loop, &fs_event);
858   ASSERT(r == 0);
859   r = uv_fs_event_start(&fs_event, fs_event_fail, ".", 0);
860   ASSERT(r == 0);
861 
862   uv_close((uv_handle_t*)&fs_event, close_cb);
863   uv_close((uv_handle_t*)handle, close_cb);
864 }
865 
866 
TEST_IMPL(fs_event_immediate_close)867 TEST_IMPL(fs_event_immediate_close) {
868 #if defined(NO_FS_EVENTS)
869   RETURN_SKIP(NO_FS_EVENTS);
870 #endif
871   uv_timer_t timer;
872   uv_loop_t* loop;
873   int r;
874 
875   loop = uv_default_loop();
876 
877   r = uv_timer_init(loop, &timer);
878   ASSERT(r == 0);
879 
880   r = uv_timer_start(&timer, timer_cb, 1, 0);
881   ASSERT(r == 0);
882 
883   uv_run(loop, UV_RUN_DEFAULT);
884 
885   ASSERT(close_cb_called == 2);
886 
887   MAKE_VALGRIND_HAPPY();
888   return 0;
889 }
890 
891 
TEST_IMPL(fs_event_close_with_pending_event)892 TEST_IMPL(fs_event_close_with_pending_event) {
893 #if defined(NO_FS_EVENTS)
894   RETURN_SKIP(NO_FS_EVENTS);
895 #endif
896   uv_loop_t* loop;
897   int r;
898 
899   loop = uv_default_loop();
900 
901   create_dir("watch_dir");
902   create_file("watch_dir/file");
903 
904   r = uv_fs_event_init(loop, &fs_event);
905   ASSERT(r == 0);
906   r = uv_fs_event_start(&fs_event, fs_event_fail, "watch_dir", 0);
907   ASSERT(r == 0);
908 
909   /* Generate an fs event. */
910   touch_file("watch_dir/file");
911 
912   uv_close((uv_handle_t*)&fs_event, close_cb);
913 
914   uv_run(loop, UV_RUN_DEFAULT);
915 
916   ASSERT(close_cb_called == 1);
917 
918   /* Clean up */
919   remove("watch_dir/file");
920   remove("watch_dir/");
921 
922   MAKE_VALGRIND_HAPPY();
923   return 0;
924 }
925 
TEST_IMPL(fs_event_close_in_callback)926 TEST_IMPL(fs_event_close_in_callback) {
927 #if defined(NO_FS_EVENTS)
928   RETURN_SKIP(NO_FS_EVENTS);
929 #elif defined(__MVS__)
930   RETURN_SKIP("Directory watching not supported on this platform.");
931 #endif
932   uv_loop_t* loop;
933   int r;
934 
935   loop = uv_default_loop();
936 
937   fs_event_unlink_files(NULL);
938   create_dir("watch_dir");
939 
940   r = uv_fs_event_init(loop, &fs_event);
941   ASSERT(r == 0);
942   r = uv_fs_event_start(&fs_event, fs_event_cb_close, "watch_dir", 0);
943   ASSERT(r == 0);
944 
945   r = uv_timer_init(loop, &timer);
946   ASSERT(r == 0);
947   r = uv_timer_start(&timer, fs_event_create_files, 100, 0);
948   ASSERT(r == 0);
949 
950   uv_run(loop, UV_RUN_DEFAULT);
951 
952   uv_close((uv_handle_t*)&timer, close_cb);
953 
954   uv_run(loop, UV_RUN_ONCE);
955 
956   ASSERT(close_cb_called == 2);
957   ASSERT(fs_event_cb_called == 3);
958 
959   /* Clean up */
960   fs_event_unlink_files(NULL);
961   remove("watch_dir/");
962 
963   MAKE_VALGRIND_HAPPY();
964   return 0;
965 }
966 
TEST_IMPL(fs_event_start_and_close)967 TEST_IMPL(fs_event_start_and_close) {
968 #if defined(NO_FS_EVENTS)
969   RETURN_SKIP(NO_FS_EVENTS);
970 #endif
971   uv_loop_t* loop;
972   uv_fs_event_t fs_event1;
973   uv_fs_event_t fs_event2;
974   int r;
975 
976   loop = uv_default_loop();
977 
978   create_dir("watch_dir");
979 
980   r = uv_fs_event_init(loop, &fs_event1);
981   ASSERT(r == 0);
982   r = uv_fs_event_start(&fs_event1, fs_event_cb_dir, "watch_dir", 0);
983   ASSERT(r == 0);
984 
985   r = uv_fs_event_init(loop, &fs_event2);
986   ASSERT(r == 0);
987   r = uv_fs_event_start(&fs_event2, fs_event_cb_dir, "watch_dir", 0);
988   ASSERT(r == 0);
989 
990   uv_close((uv_handle_t*) &fs_event2, close_cb);
991   uv_close((uv_handle_t*) &fs_event1, close_cb);
992 
993   uv_run(loop, UV_RUN_DEFAULT);
994 
995   ASSERT(close_cb_called == 2);
996 
997   remove("watch_dir/");
998   MAKE_VALGRIND_HAPPY();
999   return 0;
1000 }
1001 
TEST_IMPL(fs_event_getpath)1002 TEST_IMPL(fs_event_getpath) {
1003 #if defined(NO_FS_EVENTS)
1004   RETURN_SKIP(NO_FS_EVENTS);
1005 #endif
1006   uv_loop_t* loop = uv_default_loop();
1007   unsigned i;
1008   int r;
1009   char buf[1024];
1010   size_t len;
1011   const char* const watch_dir[] = {
1012     "watch_dir",
1013     "watch_dir/",
1014     "watch_dir///",
1015     "watch_dir/subfolder/..",
1016     "watch_dir//subfolder//..//",
1017   };
1018 
1019   create_dir("watch_dir");
1020   create_dir("watch_dir/subfolder");
1021 
1022 
1023   for (i = 0; i < ARRAY_SIZE(watch_dir); i++) {
1024     r = uv_fs_event_init(loop, &fs_event);
1025     ASSERT(r == 0);
1026     len = sizeof buf;
1027     r = uv_fs_event_getpath(&fs_event, buf, &len);
1028     ASSERT(r == UV_EINVAL);
1029     r = uv_fs_event_start(&fs_event, fail_cb, watch_dir[i], 0);
1030     ASSERT(r == 0);
1031     len = 0;
1032     r = uv_fs_event_getpath(&fs_event, buf, &len);
1033     ASSERT(r == UV_ENOBUFS);
1034     ASSERT(len < sizeof buf); /* sanity check */
1035     ASSERT(len == strlen(watch_dir[i]) + 1);
1036     r = uv_fs_event_getpath(&fs_event, buf, &len);
1037     ASSERT(r == 0);
1038     ASSERT(len == strlen(watch_dir[i]));
1039     ASSERT(strcmp(buf, watch_dir[i]) == 0);
1040     r = uv_fs_event_stop(&fs_event);
1041     ASSERT(r == 0);
1042     uv_close((uv_handle_t*) &fs_event, close_cb);
1043 
1044     uv_run(loop, UV_RUN_DEFAULT);
1045 
1046     ASSERT(close_cb_called == 1);
1047     close_cb_called = 0;
1048   }
1049 
1050   remove("watch_dir/");
1051   MAKE_VALGRIND_HAPPY();
1052   return 0;
1053 }
1054 
1055 #if defined(__APPLE__)
1056 
1057 static int fs_event_error_reported;
1058 
fs_event_error_report_cb(uv_fs_event_t * handle,const char * filename,int events,int status)1059 static void fs_event_error_report_cb(uv_fs_event_t* handle,
1060                                      const char* filename,
1061                                      int events,
1062                                      int status) {
1063   if (status != 0)
1064     fs_event_error_reported = status;
1065 }
1066 
timer_cb_nop(uv_timer_t * handle)1067 static void timer_cb_nop(uv_timer_t* handle) {
1068   ++timer_cb_called;
1069   uv_close((uv_handle_t*) handle, close_cb);
1070 }
1071 
fs_event_error_report_close_cb(uv_handle_t * handle)1072 static void fs_event_error_report_close_cb(uv_handle_t* handle) {
1073   ASSERT_NOT_NULL(handle);
1074   close_cb_called++;
1075 
1076   /* handle is allocated on-stack, no need to free it */
1077 }
1078 
1079 
TEST_IMPL(fs_event_error_reporting)1080 TEST_IMPL(fs_event_error_reporting) {
1081   unsigned int i;
1082   uv_loop_t loops[1024];
1083   uv_fs_event_t events[ARRAY_SIZE(loops)];
1084   uv_loop_t* loop;
1085   uv_fs_event_t* event;
1086 
1087   TEST_FILE_LIMIT(ARRAY_SIZE(loops) * 3);
1088 
1089   remove("watch_dir/");
1090   create_dir("watch_dir");
1091 
1092   /* Create a lot of loops, and start FSEventStream in each of them.
1093    * Eventually, this should create enough streams to make FSEventStreamStart()
1094    * fail.
1095    */
1096   for (i = 0; i < ARRAY_SIZE(loops); i++) {
1097     loop = &loops[i];
1098     ASSERT(0 == uv_loop_init(loop));
1099     event = &events[i];
1100 
1101     timer_cb_called = 0;
1102     close_cb_called = 0;
1103     ASSERT(0 == uv_fs_event_init(loop, event));
1104     ASSERT(0 == uv_fs_event_start(event,
1105                                   fs_event_error_report_cb,
1106                                   "watch_dir",
1107                                   0));
1108     uv_unref((uv_handle_t*) event);
1109 
1110     /* Let loop run for some time */
1111     ASSERT(0 == uv_timer_init(loop, &timer));
1112     ASSERT(0 == uv_timer_start(&timer, timer_cb_nop, 2, 0));
1113     uv_run(loop, UV_RUN_DEFAULT);
1114     ASSERT(1 == timer_cb_called);
1115     ASSERT(1 == close_cb_called);
1116     if (fs_event_error_reported != 0)
1117       break;
1118   }
1119 
1120   /* At least one loop should fail */
1121   ASSERT(fs_event_error_reported == UV_EMFILE);
1122 
1123   /* Stop and close all events, and destroy loops */
1124   do {
1125     loop = &loops[i];
1126     event = &events[i];
1127 
1128     ASSERT(0 == uv_fs_event_stop(event));
1129     uv_ref((uv_handle_t*) event);
1130     uv_close((uv_handle_t*) event, fs_event_error_report_close_cb);
1131 
1132     close_cb_called = 0;
1133     uv_run(loop, UV_RUN_DEFAULT);
1134     ASSERT(close_cb_called == 1);
1135 
1136     uv_loop_close(loop);
1137   } while (i-- != 0);
1138 
1139   remove("watch_dir/");
1140   MAKE_VALGRIND_HAPPY();
1141   return 0;
1142 }
1143 
1144 #else  /* !defined(__APPLE__) */
1145 
TEST_IMPL(fs_event_error_reporting)1146 TEST_IMPL(fs_event_error_reporting) {
1147   /* No-op, needed only for FSEvents backend */
1148 
1149   MAKE_VALGRIND_HAPPY();
1150   return 0;
1151 }
1152 
1153 #endif  /* defined(__APPLE__) */
1154 
TEST_IMPL(fs_event_watch_invalid_path)1155 TEST_IMPL(fs_event_watch_invalid_path) {
1156 #if defined(NO_FS_EVENTS)
1157   RETURN_SKIP(NO_FS_EVENTS);
1158 #endif
1159 
1160   uv_loop_t* loop;
1161   int r;
1162 
1163   loop = uv_default_loop();
1164   r = uv_fs_event_init(loop, &fs_event);
1165   ASSERT(r == 0);
1166   r = uv_fs_event_start(&fs_event, fs_event_cb_file, "<:;", 0);
1167   ASSERT(r != 0);
1168   ASSERT(uv_is_active((uv_handle_t*) &fs_event) == 0);
1169   r = uv_fs_event_start(&fs_event, fs_event_cb_file, "", 0);
1170   ASSERT(r != 0);
1171   ASSERT(uv_is_active((uv_handle_t*) &fs_event) == 0);
1172   MAKE_VALGRIND_HAPPY();
1173   return 0;
1174 }
1175 
1176 static int fs_event_cb_stop_calls;
1177 
fs_event_cb_stop(uv_fs_event_t * handle,const char * path,int events,int status)1178 static void fs_event_cb_stop(uv_fs_event_t* handle, const char* path,
1179                              int events, int status) {
1180   uv_fs_event_stop(handle);
1181   fs_event_cb_stop_calls++;
1182 }
1183 
TEST_IMPL(fs_event_stop_in_cb)1184 TEST_IMPL(fs_event_stop_in_cb) {
1185   uv_fs_event_t fs;
1186   uv_timer_t timer;
1187   char path[] = "fs_event_stop_in_cb.txt";
1188 
1189 #if defined(NO_FS_EVENTS)
1190   RETURN_SKIP(NO_FS_EVENTS);
1191 #endif
1192 
1193   remove(path);
1194   create_file(path);
1195 
1196   ASSERT_EQ(0, uv_fs_event_init(uv_default_loop(), &fs));
1197   ASSERT_EQ(0, uv_fs_event_start(&fs, fs_event_cb_stop, path, 0));
1198 
1199   /* Note: timer_cb_touch() closes the handle. */
1200   timer.data = path;
1201   ASSERT_EQ(0, uv_timer_init(uv_default_loop(), &timer));
1202   ASSERT_EQ(0, uv_timer_start(&timer, timer_cb_touch, 100, 0));
1203 
1204   ASSERT_EQ(0, fs_event_cb_stop_calls);
1205   ASSERT_EQ(0, timer_cb_touch_called);
1206 
1207   ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT));
1208 
1209   ASSERT_EQ(1, fs_event_cb_stop_calls);
1210   ASSERT_EQ(1, timer_cb_touch_called);
1211 
1212   uv_close((uv_handle_t*) &fs, NULL);
1213   ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT));
1214   ASSERT_EQ(1, fs_event_cb_stop_calls);
1215 
1216   remove(path);
1217 
1218   MAKE_VALGRIND_HAPPY();
1219   return 0;
1220 }
1221