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