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