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