xref: /netbsd-src/external/mit/libuv/dist/test/test-fs-event.c (revision 122b5006ee1bd67145794b4cde92f4fe4781a5ec)
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 
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 
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 
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 
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 
120 static void close_cb(uv_handle_t* handle) {
121   ASSERT(handle != NULL);
122   close_cb_called++;
123 }
124 
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 
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 
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 
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 
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 
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)
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 
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 
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 
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 
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 
337 static void timer_cb_close_handle(uv_timer_t* timer) {
338   uv_handle_t* handle;
339 
340   ASSERT(timer != NULL);
341   handle = timer->data;
342 
343   uv_close((uv_handle_t*)timer, NULL);
344   uv_close((uv_handle_t*)handle, close_cb);
345 }
346 
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 
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 
381 static void timer_cb_touch(uv_timer_t* timer) {
382   uv_close((uv_handle_t*)timer, NULL);
383   touch_file("watch_file");
384   timer_cb_touch_called++;
385 }
386 
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 
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 
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 
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 
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
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 
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 
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 
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 
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   r = uv_timer_start(&timer, timer_cb_touch, 1100, 0);
731   ASSERT(r == 0);
732 
733   ASSERT(timer_cb_touch_called == 0);
734   ASSERT(fs_event_cb_called == 0);
735   ASSERT(close_cb_called == 0);
736 
737   uv_run(loop, UV_RUN_DEFAULT);
738 
739   ASSERT(timer_cb_touch_called == 1);
740   ASSERT(fs_event_cb_called == 1);
741   ASSERT(close_cb_called == 1);
742 
743   /* Cleanup */
744   remove("watch_file");
745 
746   MAKE_VALGRIND_HAPPY();
747   return 0;
748 }
749 
750 #ifdef _WIN32
751 TEST_IMPL(fs_event_watch_file_root_dir) {
752   uv_loop_t* loop;
753   int r;
754 
755   const char* sys_drive = getenv("SystemDrive");
756   char path[] = "\\\\?\\X:\\bootsect.bak";
757 
758   ASSERT(sys_drive != NULL);
759   strncpy(path + sizeof("\\\\?\\") - 1, sys_drive, 1);
760 
761   loop = uv_default_loop();
762 
763   r = uv_fs_event_init(loop, &fs_event);
764   ASSERT(r == 0);
765   r = uv_fs_event_start(&fs_event, fail_cb, path, 0);
766   if (r == UV_ENOENT)
767     RETURN_SKIP("bootsect.bak doesn't exist in system root.\n");
768   ASSERT(r == 0);
769 
770   uv_close((uv_handle_t*) &fs_event, NULL);
771 
772   MAKE_VALGRIND_HAPPY();
773   return 0;
774 }
775 #endif
776 
777 TEST_IMPL(fs_event_no_callback_after_close) {
778 #if defined(NO_FS_EVENTS)
779   RETURN_SKIP(NO_FS_EVENTS);
780 #endif
781 
782   uv_loop_t* loop = uv_default_loop();
783   int r;
784 
785   /* Setup */
786   remove("watch_dir/file1");
787   remove("watch_dir/");
788   create_dir("watch_dir");
789   create_file("watch_dir/file1");
790 
791   r = uv_fs_event_init(loop, &fs_event);
792   ASSERT(r == 0);
793   r = uv_fs_event_start(&fs_event,
794                         fs_event_cb_file,
795                         "watch_dir/file1",
796                         0);
797   ASSERT(r == 0);
798 
799 
800   uv_close((uv_handle_t*)&fs_event, close_cb);
801   touch_file("watch_dir/file1");
802   uv_run(loop, UV_RUN_DEFAULT);
803 
804   ASSERT(fs_event_cb_called == 0);
805   ASSERT(close_cb_called == 1);
806 
807   /* Cleanup */
808   remove("watch_dir/file1");
809   remove("watch_dir/");
810 
811   MAKE_VALGRIND_HAPPY();
812   return 0;
813 }
814 
815 TEST_IMPL(fs_event_no_callback_on_close) {
816 #if defined(NO_FS_EVENTS)
817   RETURN_SKIP(NO_FS_EVENTS);
818 #endif
819 
820   uv_loop_t* loop = uv_default_loop();
821   int r;
822 
823   /* Setup */
824   remove("watch_dir/file1");
825   remove("watch_dir/");
826   create_dir("watch_dir");
827   create_file("watch_dir/file1");
828 
829   r = uv_fs_event_init(loop, &fs_event);
830   ASSERT(r == 0);
831   r = uv_fs_event_start(&fs_event,
832                         fs_event_cb_file,
833                         "watch_dir/file1",
834                         0);
835   ASSERT(r == 0);
836 
837   uv_close((uv_handle_t*)&fs_event, close_cb);
838 
839   uv_run(loop, UV_RUN_DEFAULT);
840 
841   ASSERT(fs_event_cb_called == 0);
842   ASSERT(close_cb_called == 1);
843 
844   /* Cleanup */
845   remove("watch_dir/file1");
846   remove("watch_dir/");
847 
848   MAKE_VALGRIND_HAPPY();
849   return 0;
850 }
851 
852 
853 static void timer_cb(uv_timer_t* handle) {
854   int r;
855 
856   r = uv_fs_event_init(handle->loop, &fs_event);
857   ASSERT(r == 0);
858   r = uv_fs_event_start(&fs_event, fs_event_fail, ".", 0);
859   ASSERT(r == 0);
860 
861   uv_close((uv_handle_t*)&fs_event, close_cb);
862   uv_close((uv_handle_t*)handle, close_cb);
863 }
864 
865 
866 TEST_IMPL(fs_event_immediate_close) {
867 #if defined(NO_FS_EVENTS)
868   RETURN_SKIP(NO_FS_EVENTS);
869 #endif
870   uv_timer_t timer;
871   uv_loop_t* loop;
872   int r;
873 
874   loop = uv_default_loop();
875 
876   r = uv_timer_init(loop, &timer);
877   ASSERT(r == 0);
878 
879   r = uv_timer_start(&timer, timer_cb, 1, 0);
880   ASSERT(r == 0);
881 
882   uv_run(loop, UV_RUN_DEFAULT);
883 
884   ASSERT(close_cb_called == 2);
885 
886   MAKE_VALGRIND_HAPPY();
887   return 0;
888 }
889 
890 
891 TEST_IMPL(fs_event_close_with_pending_event) {
892 #if defined(NO_FS_EVENTS)
893   RETURN_SKIP(NO_FS_EVENTS);
894 #endif
895   uv_loop_t* loop;
896   int r;
897 
898   loop = uv_default_loop();
899 
900   create_dir("watch_dir");
901   create_file("watch_dir/file");
902 
903   r = uv_fs_event_init(loop, &fs_event);
904   ASSERT(r == 0);
905   r = uv_fs_event_start(&fs_event, fs_event_fail, "watch_dir", 0);
906   ASSERT(r == 0);
907 
908   /* Generate an fs event. */
909   touch_file("watch_dir/file");
910 
911   uv_close((uv_handle_t*)&fs_event, close_cb);
912 
913   uv_run(loop, UV_RUN_DEFAULT);
914 
915   ASSERT(close_cb_called == 1);
916 
917   /* Clean up */
918   remove("watch_dir/file");
919   remove("watch_dir/");
920 
921   MAKE_VALGRIND_HAPPY();
922   return 0;
923 }
924 
925 TEST_IMPL(fs_event_close_in_callback) {
926 #if defined(NO_FS_EVENTS)
927   RETURN_SKIP(NO_FS_EVENTS);
928 #elif defined(__MVS__)
929   RETURN_SKIP("Directory watching not supported on this platform.");
930 #endif
931   uv_loop_t* loop;
932   int r;
933 
934   loop = uv_default_loop();
935 
936   fs_event_unlink_files(NULL);
937   create_dir("watch_dir");
938 
939   r = uv_fs_event_init(loop, &fs_event);
940   ASSERT(r == 0);
941   r = uv_fs_event_start(&fs_event, fs_event_cb_close, "watch_dir", 0);
942   ASSERT(r == 0);
943 
944   r = uv_timer_init(loop, &timer);
945   ASSERT(r == 0);
946   r = uv_timer_start(&timer, fs_event_create_files, 100, 0);
947   ASSERT(r == 0);
948 
949   uv_run(loop, UV_RUN_DEFAULT);
950 
951   uv_close((uv_handle_t*)&timer, close_cb);
952 
953   uv_run(loop, UV_RUN_ONCE);
954 
955   ASSERT(close_cb_called == 2);
956   ASSERT(fs_event_cb_called == 3);
957 
958   /* Clean up */
959   fs_event_unlink_files(NULL);
960   remove("watch_dir/");
961 
962   MAKE_VALGRIND_HAPPY();
963   return 0;
964 }
965 
966 TEST_IMPL(fs_event_start_and_close) {
967 #if defined(NO_FS_EVENTS)
968   RETURN_SKIP(NO_FS_EVENTS);
969 #endif
970   uv_loop_t* loop;
971   uv_fs_event_t fs_event1;
972   uv_fs_event_t fs_event2;
973   int r;
974 
975   loop = uv_default_loop();
976 
977   create_dir("watch_dir");
978 
979   r = uv_fs_event_init(loop, &fs_event1);
980   ASSERT(r == 0);
981   r = uv_fs_event_start(&fs_event1, fs_event_cb_dir, "watch_dir", 0);
982   ASSERT(r == 0);
983 
984   r = uv_fs_event_init(loop, &fs_event2);
985   ASSERT(r == 0);
986   r = uv_fs_event_start(&fs_event2, fs_event_cb_dir, "watch_dir", 0);
987   ASSERT(r == 0);
988 
989   uv_close((uv_handle_t*) &fs_event2, close_cb);
990   uv_close((uv_handle_t*) &fs_event1, close_cb);
991 
992   uv_run(loop, UV_RUN_DEFAULT);
993 
994   ASSERT(close_cb_called == 2);
995 
996   remove("watch_dir/");
997   MAKE_VALGRIND_HAPPY();
998   return 0;
999 }
1000 
1001 TEST_IMPL(fs_event_getpath) {
1002 #if defined(NO_FS_EVENTS)
1003   RETURN_SKIP(NO_FS_EVENTS);
1004 #endif
1005   uv_loop_t* loop = uv_default_loop();
1006   unsigned i;
1007   int r;
1008   char buf[1024];
1009   size_t len;
1010   const char* const watch_dir[] = {
1011     "watch_dir",
1012     "watch_dir/",
1013     "watch_dir///",
1014     "watch_dir/subfolder/..",
1015     "watch_dir//subfolder//..//",
1016   };
1017 
1018   create_dir("watch_dir");
1019   create_dir("watch_dir/subfolder");
1020 
1021 
1022   for (i = 0; i < ARRAY_SIZE(watch_dir); i++) {
1023     r = uv_fs_event_init(loop, &fs_event);
1024     ASSERT(r == 0);
1025     len = sizeof buf;
1026     r = uv_fs_event_getpath(&fs_event, buf, &len);
1027     ASSERT(r == UV_EINVAL);
1028     r = uv_fs_event_start(&fs_event, fail_cb, watch_dir[i], 0);
1029     ASSERT(r == 0);
1030     len = 0;
1031     r = uv_fs_event_getpath(&fs_event, buf, &len);
1032     ASSERT(r == UV_ENOBUFS);
1033     ASSERT(len < sizeof buf); /* sanity check */
1034     ASSERT(len == strlen(watch_dir[i]) + 1);
1035     r = uv_fs_event_getpath(&fs_event, buf, &len);
1036     ASSERT(r == 0);
1037     ASSERT(len == strlen(watch_dir[i]));
1038     ASSERT(strcmp(buf, watch_dir[i]) == 0);
1039     r = uv_fs_event_stop(&fs_event);
1040     ASSERT(r == 0);
1041     uv_close((uv_handle_t*) &fs_event, close_cb);
1042 
1043     uv_run(loop, UV_RUN_DEFAULT);
1044 
1045     ASSERT(close_cb_called == 1);
1046     close_cb_called = 0;
1047   }
1048 
1049   remove("watch_dir/");
1050   MAKE_VALGRIND_HAPPY();
1051   return 0;
1052 }
1053 
1054 #if defined(__APPLE__)
1055 
1056 static int fs_event_error_reported;
1057 
1058 static void fs_event_error_report_cb(uv_fs_event_t* handle,
1059                                      const char* filename,
1060                                      int events,
1061                                      int status) {
1062   if (status != 0)
1063     fs_event_error_reported = status;
1064 }
1065 
1066 static void timer_cb_nop(uv_timer_t* handle) {
1067   ++timer_cb_called;
1068   uv_close((uv_handle_t*) handle, close_cb);
1069 }
1070 
1071 static void fs_event_error_report_close_cb(uv_handle_t* handle) {
1072   ASSERT(handle != NULL);
1073   close_cb_called++;
1074 
1075   /* handle is allocated on-stack, no need to free it */
1076 }
1077 
1078 
1079 TEST_IMPL(fs_event_error_reporting) {
1080   unsigned int i;
1081   uv_loop_t loops[1024];
1082   uv_fs_event_t events[ARRAY_SIZE(loops)];
1083   uv_loop_t* loop;
1084   uv_fs_event_t* event;
1085 
1086   TEST_FILE_LIMIT(ARRAY_SIZE(loops) * 3);
1087 
1088   remove("watch_dir/");
1089   create_dir("watch_dir");
1090 
1091   /* Create a lot of loops, and start FSEventStream in each of them.
1092    * Eventually, this should create enough streams to make FSEventStreamStart()
1093    * fail.
1094    */
1095   for (i = 0; i < ARRAY_SIZE(loops); i++) {
1096     loop = &loops[i];
1097     ASSERT(0 == uv_loop_init(loop));
1098     event = &events[i];
1099 
1100     timer_cb_called = 0;
1101     close_cb_called = 0;
1102     ASSERT(0 == uv_fs_event_init(loop, event));
1103     ASSERT(0 == uv_fs_event_start(event,
1104                                   fs_event_error_report_cb,
1105                                   "watch_dir",
1106                                   0));
1107     uv_unref((uv_handle_t*) event);
1108 
1109     /* Let loop run for some time */
1110     ASSERT(0 == uv_timer_init(loop, &timer));
1111     ASSERT(0 == uv_timer_start(&timer, timer_cb_nop, 2, 0));
1112     uv_run(loop, UV_RUN_DEFAULT);
1113     ASSERT(1 == timer_cb_called);
1114     ASSERT(1 == close_cb_called);
1115     if (fs_event_error_reported != 0)
1116       break;
1117   }
1118 
1119   /* At least one loop should fail */
1120   ASSERT(fs_event_error_reported == UV_EMFILE);
1121 
1122   /* Stop and close all events, and destroy loops */
1123   do {
1124     loop = &loops[i];
1125     event = &events[i];
1126 
1127     ASSERT(0 == uv_fs_event_stop(event));
1128     uv_ref((uv_handle_t*) event);
1129     uv_close((uv_handle_t*) event, fs_event_error_report_close_cb);
1130 
1131     close_cb_called = 0;
1132     uv_run(loop, UV_RUN_DEFAULT);
1133     ASSERT(close_cb_called == 1);
1134 
1135     uv_loop_close(loop);
1136   } while (i-- != 0);
1137 
1138   remove("watch_dir/");
1139   MAKE_VALGRIND_HAPPY();
1140   return 0;
1141 }
1142 
1143 #else  /* !defined(__APPLE__) */
1144 
1145 TEST_IMPL(fs_event_error_reporting) {
1146   /* No-op, needed only for FSEvents backend */
1147 
1148   MAKE_VALGRIND_HAPPY();
1149   return 0;
1150 }
1151 
1152 #endif  /* defined(__APPLE__) */
1153 
1154 TEST_IMPL(fs_event_watch_invalid_path) {
1155 #if defined(NO_FS_EVENTS)
1156   RETURN_SKIP(NO_FS_EVENTS);
1157 #endif
1158 
1159   uv_loop_t* loop;
1160   int r;
1161 
1162   loop = uv_default_loop();
1163   r = uv_fs_event_init(loop, &fs_event);
1164   ASSERT(r == 0);
1165   r = uv_fs_event_start(&fs_event, fs_event_cb_file, "<:;", 0);
1166   ASSERT(r != 0);
1167   ASSERT(uv_is_active((uv_handle_t*) &fs_event) == 0);
1168   r = uv_fs_event_start(&fs_event, fs_event_cb_file, "", 0);
1169   ASSERT(r != 0);
1170   ASSERT(uv_is_active((uv_handle_t*) &fs_event) == 0);
1171   MAKE_VALGRIND_HAPPY();
1172   return 0;
1173 }
1174