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