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