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