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