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 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 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 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 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 120 static void close_cb(uv_handle_t* handle) { 121 ASSERT(handle != NULL); 122 close_cb_called++; 123 } 124 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 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 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 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 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 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) 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 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 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 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 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 337 static void timer_cb_close_handle(uv_timer_t* timer) { 338 uv_handle_t* handle; 339 340 ASSERT(timer != NULL); 341 handle = timer->data; 342 343 uv_close((uv_handle_t*)timer, NULL); 344 uv_close((uv_handle_t*)handle, close_cb); 345 } 346 347 static void fs_event_cb_file_current_dir(uv_fs_event_t* handle, 348 const char* filename, int events, int status) { 349 ASSERT(fs_event_cb_called == 0); 350 ++fs_event_cb_called; 351 352 ASSERT(handle == &fs_event); 353 ASSERT(status == 0); 354 ASSERT(events == UV_CHANGE); 355 #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) 356 ASSERT(strcmp(filename, "watch_file") == 0); 357 #else 358 ASSERT(filename == NULL || strcmp(filename, "watch_file") == 0); 359 #endif 360 361 /* Regression test for SunOS: touch should generate just one event. */ 362 { 363 static uv_timer_t timer; 364 uv_timer_init(handle->loop, &timer); 365 timer.data = handle; 366 uv_timer_start(&timer, timer_cb_close_handle, 250, 0); 367 } 368 } 369 370 static void timer_cb_file(uv_timer_t* handle) { 371 ++timer_cb_called; 372 373 if (timer_cb_called == 1) { 374 touch_file("watch_dir/file1"); 375 } else { 376 touch_file("watch_dir/file2"); 377 uv_close((uv_handle_t*)handle, close_cb); 378 } 379 } 380 381 static void timer_cb_touch(uv_timer_t* timer) { 382 uv_close((uv_handle_t*)timer, NULL); 383 touch_file("watch_file"); 384 timer_cb_touch_called++; 385 } 386 387 static void timer_cb_exact(uv_timer_t* handle) { 388 int r; 389 390 if (timer_cb_exact_called == 0) { 391 touch_file("watch_dir/file.js"); 392 } else { 393 uv_close((uv_handle_t*)handle, NULL); 394 r = uv_fs_event_stop(&fs_event); 395 ASSERT(r == 0); 396 uv_close((uv_handle_t*) &fs_event, NULL); 397 } 398 399 ++timer_cb_exact_called; 400 } 401 402 static void timer_cb_watch_twice(uv_timer_t* handle) { 403 uv_fs_event_t* handles = handle->data; 404 uv_close((uv_handle_t*) (handles + 0), NULL); 405 uv_close((uv_handle_t*) (handles + 1), NULL); 406 uv_close((uv_handle_t*) handle, NULL); 407 } 408 409 static void fs_event_cb_close(uv_fs_event_t* handle, 410 const char* filename, 411 int events, 412 int status) { 413 ASSERT(status == 0); 414 415 ASSERT(fs_event_cb_called < 3); 416 ++fs_event_cb_called; 417 418 if (fs_event_cb_called == 3) { 419 uv_close((uv_handle_t*) handle, close_cb); 420 } 421 } 422 423 424 TEST_IMPL(fs_event_watch_dir) { 425 #if defined(NO_FS_EVENTS) 426 RETURN_SKIP(NO_FS_EVENTS); 427 #elif defined(__MVS__) 428 RETURN_SKIP("Directory watching not supported on this platform."); 429 #endif 430 431 uv_loop_t* loop = uv_default_loop(); 432 int r; 433 434 /* Setup */ 435 fs_event_unlink_files(NULL); 436 remove("watch_dir/file2"); 437 remove("watch_dir/file1"); 438 remove("watch_dir/"); 439 create_dir("watch_dir"); 440 441 r = uv_fs_event_init(loop, &fs_event); 442 ASSERT(r == 0); 443 r = uv_fs_event_start(&fs_event, fs_event_cb_dir_multi_file, "watch_dir", 0); 444 ASSERT(r == 0); 445 r = uv_timer_init(loop, &timer); 446 ASSERT(r == 0); 447 r = uv_timer_start(&timer, fs_event_create_files, 100, 0); 448 ASSERT(r == 0); 449 450 uv_run(loop, UV_RUN_DEFAULT); 451 452 ASSERT(fs_event_cb_called == fs_event_created + fs_event_removed); 453 ASSERT(close_cb_called == 2); 454 455 /* Cleanup */ 456 fs_event_unlink_files(NULL); 457 remove("watch_dir/file2"); 458 remove("watch_dir/file1"); 459 remove("watch_dir/"); 460 461 MAKE_VALGRIND_HAPPY(); 462 return 0; 463 } 464 465 466 TEST_IMPL(fs_event_watch_dir_recursive) { 467 #if defined(__APPLE__) || defined(_WIN32) 468 uv_loop_t* loop; 469 int r; 470 uv_fs_event_t fs_event_root; 471 472 /* Setup */ 473 loop = uv_default_loop(); 474 fs_event_unlink_files(NULL); 475 remove("watch_dir/file2"); 476 remove("watch_dir/file1"); 477 remove("watch_dir/subdir"); 478 remove("watch_dir/"); 479 create_dir("watch_dir"); 480 create_dir("watch_dir/subdir"); 481 482 r = uv_fs_event_init(loop, &fs_event); 483 ASSERT(r == 0); 484 r = uv_fs_event_start(&fs_event, 485 fs_event_cb_dir_multi_file_in_subdir, 486 "watch_dir", 487 UV_FS_EVENT_RECURSIVE); 488 ASSERT(r == 0); 489 r = uv_timer_init(loop, &timer); 490 ASSERT(r == 0); 491 r = uv_timer_start(&timer, fs_event_create_files_in_subdir, 100, 0); 492 ASSERT(r == 0); 493 494 #ifndef _WIN32 495 /* Also try to watch the root directory. 496 * This will be noisier, so we're just checking for any couple events to happen. */ 497 r = uv_fs_event_init(loop, &fs_event_root); 498 ASSERT(r == 0); 499 r = uv_fs_event_start(&fs_event_root, 500 fs_event_cb_close, 501 "/", 502 UV_FS_EVENT_RECURSIVE); 503 ASSERT(r == 0); 504 #else 505 fs_event_cb_called += 3; 506 close_cb_called += 1; 507 (void)fs_event_root; 508 #endif 509 510 uv_run(loop, UV_RUN_DEFAULT); 511 512 ASSERT(fs_multievent_cb_called == fs_event_created + fs_event_removed); 513 ASSERT(fs_event_cb_called == 3); 514 ASSERT(close_cb_called == 3); 515 516 /* Cleanup */ 517 fs_event_unlink_files_in_subdir(NULL); 518 remove("watch_dir/file2"); 519 remove("watch_dir/file1"); 520 remove("watch_dir/subdir"); 521 remove("watch_dir/"); 522 523 MAKE_VALGRIND_HAPPY(); 524 return 0; 525 #else 526 RETURN_SKIP("Recursive directory watching not supported on this platform."); 527 #endif 528 } 529 530 #ifdef _WIN32 531 TEST_IMPL(fs_event_watch_dir_short_path) { 532 uv_loop_t* loop; 533 uv_fs_t req; 534 int has_shortnames; 535 int r; 536 537 /* Setup */ 538 loop = uv_default_loop(); 539 remove("watch_dir/file1"); 540 remove("watch_dir/"); 541 create_dir("watch_dir"); 542 create_file("watch_dir/file1"); 543 544 /* Newer version of Windows ship with 545 HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\NtfsDisable8dot3NameCreation 546 not equal to 0. So we verify the files we created are addressable by a 8.3 547 short name */ 548 has_shortnames = uv_fs_stat(NULL, &req, "watch_~1", NULL) != UV_ENOENT; 549 if (has_shortnames) { 550 r = uv_fs_event_init(loop, &fs_event); 551 ASSERT(r == 0); 552 r = uv_fs_event_start(&fs_event, fs_event_cb_dir, "watch_~1", 0); 553 ASSERT(r == 0); 554 r = uv_timer_init(loop, &timer); 555 ASSERT(r == 0); 556 r = uv_timer_start(&timer, timer_cb_file, 100, 0); 557 ASSERT(r == 0); 558 559 uv_run(loop, UV_RUN_DEFAULT); 560 561 ASSERT(fs_event_cb_called == 1); 562 ASSERT(timer_cb_called == 1); 563 ASSERT(close_cb_called == 1); 564 } 565 566 /* Cleanup */ 567 remove("watch_dir/file1"); 568 remove("watch_dir/"); 569 570 MAKE_VALGRIND_HAPPY(); 571 572 if (!has_shortnames) 573 RETURN_SKIP("Was not able to address files with 8.3 short name."); 574 575 return 0; 576 } 577 #endif 578 579 580 TEST_IMPL(fs_event_watch_file) { 581 #if defined(NO_FS_EVENTS) 582 RETURN_SKIP(NO_FS_EVENTS); 583 #endif 584 585 uv_loop_t* loop = uv_default_loop(); 586 int r; 587 588 /* Setup */ 589 remove("watch_dir/file2"); 590 remove("watch_dir/file1"); 591 remove("watch_dir/"); 592 create_dir("watch_dir"); 593 create_file("watch_dir/file1"); 594 create_file("watch_dir/file2"); 595 596 r = uv_fs_event_init(loop, &fs_event); 597 ASSERT(r == 0); 598 r = uv_fs_event_start(&fs_event, fs_event_cb_file, "watch_dir/file2", 0); 599 ASSERT(r == 0); 600 r = uv_timer_init(loop, &timer); 601 ASSERT(r == 0); 602 r = uv_timer_start(&timer, timer_cb_file, 100, 100); 603 ASSERT(r == 0); 604 605 uv_run(loop, UV_RUN_DEFAULT); 606 607 ASSERT(fs_event_cb_called == 1); 608 ASSERT(timer_cb_called == 2); 609 ASSERT(close_cb_called == 2); 610 611 /* Cleanup */ 612 remove("watch_dir/file2"); 613 remove("watch_dir/file1"); 614 remove("watch_dir/"); 615 616 MAKE_VALGRIND_HAPPY(); 617 return 0; 618 } 619 620 TEST_IMPL(fs_event_watch_file_exact_path) { 621 /* 622 This test watches a file named "file.jsx" and modifies a file named 623 "file.js". The test verifies that no events occur for file.jsx. 624 */ 625 626 #if defined(NO_FS_EVENTS) 627 RETURN_SKIP(NO_FS_EVENTS); 628 #endif 629 630 uv_loop_t* loop; 631 int r; 632 633 loop = uv_default_loop(); 634 635 /* Setup */ 636 remove("watch_dir/file.js"); 637 remove("watch_dir/file.jsx"); 638 remove("watch_dir/"); 639 create_dir("watch_dir"); 640 create_file("watch_dir/file.js"); 641 create_file("watch_dir/file.jsx"); 642 #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_12) 643 /* Empirically, FSEvents seems to (reliably) report the preceeding 644 * create_file events prior to macOS 10.11.6 in the subsequent fs_watch 645 * creation, but that behavior hasn't been observed to occur on newer 646 * versions. Give a long delay here to let the system settle before running 647 * the test. */ 648 uv_sleep(1100); 649 uv_update_time(loop); 650 #endif 651 652 r = uv_fs_event_init(loop, &fs_event); 653 ASSERT(r == 0); 654 r = uv_fs_event_start(&fs_event, fs_event_fail, "watch_dir/file.jsx", 0); 655 ASSERT(r == 0); 656 r = uv_timer_init(loop, &timer); 657 ASSERT(r == 0); 658 r = uv_timer_start(&timer, timer_cb_exact, 100, 100); 659 ASSERT(r == 0); 660 r = uv_run(loop, UV_RUN_DEFAULT); 661 ASSERT(r == 0); 662 ASSERT(timer_cb_exact_called == 2); 663 664 /* Cleanup */ 665 remove("watch_dir/file.js"); 666 remove("watch_dir/file.jsx"); 667 remove("watch_dir/"); 668 669 MAKE_VALGRIND_HAPPY(); 670 return 0; 671 } 672 673 TEST_IMPL(fs_event_watch_file_twice) { 674 #if defined(NO_FS_EVENTS) 675 RETURN_SKIP(NO_FS_EVENTS); 676 #endif 677 const char path[] = "test/fixtures/empty_file"; 678 uv_fs_event_t watchers[2]; 679 uv_timer_t timer; 680 uv_loop_t* loop; 681 682 loop = uv_default_loop(); 683 timer.data = watchers; 684 685 ASSERT(0 == uv_fs_event_init(loop, watchers + 0)); 686 ASSERT(0 == uv_fs_event_start(watchers + 0, fail_cb, path, 0)); 687 ASSERT(0 == uv_fs_event_init(loop, watchers + 1)); 688 ASSERT(0 == uv_fs_event_start(watchers + 1, fail_cb, path, 0)); 689 ASSERT(0 == uv_timer_init(loop, &timer)); 690 ASSERT(0 == uv_timer_start(&timer, timer_cb_watch_twice, 10, 0)); 691 ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); 692 693 MAKE_VALGRIND_HAPPY(); 694 return 0; 695 } 696 697 TEST_IMPL(fs_event_watch_file_current_dir) { 698 #if defined(NO_FS_EVENTS) 699 RETURN_SKIP(NO_FS_EVENTS); 700 #endif 701 uv_timer_t timer; 702 uv_loop_t* loop; 703 int r; 704 705 loop = uv_default_loop(); 706 707 /* Setup */ 708 remove("watch_file"); 709 create_file("watch_file"); 710 #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_12) 711 /* Empirically, kevent seems to (sometimes) report the preceeding 712 * create_file events prior to macOS 10.11.6 in the subsequent fs_event_start 713 * So let the system settle before running the test. */ 714 uv_sleep(1100); 715 uv_update_time(loop); 716 #endif 717 718 r = uv_fs_event_init(loop, &fs_event); 719 ASSERT(r == 0); 720 r = uv_fs_event_start(&fs_event, 721 fs_event_cb_file_current_dir, 722 "watch_file", 723 0); 724 ASSERT(r == 0); 725 726 727 r = uv_timer_init(loop, &timer); 728 ASSERT(r == 0); 729 730 r = uv_timer_start(&timer, timer_cb_touch, 1100, 0); 731 ASSERT(r == 0); 732 733 ASSERT(timer_cb_touch_called == 0); 734 ASSERT(fs_event_cb_called == 0); 735 ASSERT(close_cb_called == 0); 736 737 uv_run(loop, UV_RUN_DEFAULT); 738 739 ASSERT(timer_cb_touch_called == 1); 740 ASSERT(fs_event_cb_called == 1); 741 ASSERT(close_cb_called == 1); 742 743 /* Cleanup */ 744 remove("watch_file"); 745 746 MAKE_VALGRIND_HAPPY(); 747 return 0; 748 } 749 750 #ifdef _WIN32 751 TEST_IMPL(fs_event_watch_file_root_dir) { 752 uv_loop_t* loop; 753 int r; 754 755 const char* sys_drive = getenv("SystemDrive"); 756 char path[] = "\\\\?\\X:\\bootsect.bak"; 757 758 ASSERT(sys_drive != NULL); 759 strncpy(path + sizeof("\\\\?\\") - 1, sys_drive, 1); 760 761 loop = uv_default_loop(); 762 763 r = uv_fs_event_init(loop, &fs_event); 764 ASSERT(r == 0); 765 r = uv_fs_event_start(&fs_event, fail_cb, path, 0); 766 if (r == UV_ENOENT) 767 RETURN_SKIP("bootsect.bak doesn't exist in system root.\n"); 768 ASSERT(r == 0); 769 770 uv_close((uv_handle_t*) &fs_event, NULL); 771 772 MAKE_VALGRIND_HAPPY(); 773 return 0; 774 } 775 #endif 776 777 TEST_IMPL(fs_event_no_callback_after_close) { 778 #if defined(NO_FS_EVENTS) 779 RETURN_SKIP(NO_FS_EVENTS); 780 #endif 781 782 uv_loop_t* loop = uv_default_loop(); 783 int r; 784 785 /* Setup */ 786 remove("watch_dir/file1"); 787 remove("watch_dir/"); 788 create_dir("watch_dir"); 789 create_file("watch_dir/file1"); 790 791 r = uv_fs_event_init(loop, &fs_event); 792 ASSERT(r == 0); 793 r = uv_fs_event_start(&fs_event, 794 fs_event_cb_file, 795 "watch_dir/file1", 796 0); 797 ASSERT(r == 0); 798 799 800 uv_close((uv_handle_t*)&fs_event, close_cb); 801 touch_file("watch_dir/file1"); 802 uv_run(loop, UV_RUN_DEFAULT); 803 804 ASSERT(fs_event_cb_called == 0); 805 ASSERT(close_cb_called == 1); 806 807 /* Cleanup */ 808 remove("watch_dir/file1"); 809 remove("watch_dir/"); 810 811 MAKE_VALGRIND_HAPPY(); 812 return 0; 813 } 814 815 TEST_IMPL(fs_event_no_callback_on_close) { 816 #if defined(NO_FS_EVENTS) 817 RETURN_SKIP(NO_FS_EVENTS); 818 #endif 819 820 uv_loop_t* loop = uv_default_loop(); 821 int r; 822 823 /* Setup */ 824 remove("watch_dir/file1"); 825 remove("watch_dir/"); 826 create_dir("watch_dir"); 827 create_file("watch_dir/file1"); 828 829 r = uv_fs_event_init(loop, &fs_event); 830 ASSERT(r == 0); 831 r = uv_fs_event_start(&fs_event, 832 fs_event_cb_file, 833 "watch_dir/file1", 834 0); 835 ASSERT(r == 0); 836 837 uv_close((uv_handle_t*)&fs_event, close_cb); 838 839 uv_run(loop, UV_RUN_DEFAULT); 840 841 ASSERT(fs_event_cb_called == 0); 842 ASSERT(close_cb_called == 1); 843 844 /* Cleanup */ 845 remove("watch_dir/file1"); 846 remove("watch_dir/"); 847 848 MAKE_VALGRIND_HAPPY(); 849 return 0; 850 } 851 852 853 static void timer_cb(uv_timer_t* handle) { 854 int r; 855 856 r = uv_fs_event_init(handle->loop, &fs_event); 857 ASSERT(r == 0); 858 r = uv_fs_event_start(&fs_event, fs_event_fail, ".", 0); 859 ASSERT(r == 0); 860 861 uv_close((uv_handle_t*)&fs_event, close_cb); 862 uv_close((uv_handle_t*)handle, close_cb); 863 } 864 865 866 TEST_IMPL(fs_event_immediate_close) { 867 #if defined(NO_FS_EVENTS) 868 RETURN_SKIP(NO_FS_EVENTS); 869 #endif 870 uv_timer_t timer; 871 uv_loop_t* loop; 872 int r; 873 874 loop = uv_default_loop(); 875 876 r = uv_timer_init(loop, &timer); 877 ASSERT(r == 0); 878 879 r = uv_timer_start(&timer, timer_cb, 1, 0); 880 ASSERT(r == 0); 881 882 uv_run(loop, UV_RUN_DEFAULT); 883 884 ASSERT(close_cb_called == 2); 885 886 MAKE_VALGRIND_HAPPY(); 887 return 0; 888 } 889 890 891 TEST_IMPL(fs_event_close_with_pending_event) { 892 #if defined(NO_FS_EVENTS) 893 RETURN_SKIP(NO_FS_EVENTS); 894 #endif 895 uv_loop_t* loop; 896 int r; 897 898 loop = uv_default_loop(); 899 900 create_dir("watch_dir"); 901 create_file("watch_dir/file"); 902 903 r = uv_fs_event_init(loop, &fs_event); 904 ASSERT(r == 0); 905 r = uv_fs_event_start(&fs_event, fs_event_fail, "watch_dir", 0); 906 ASSERT(r == 0); 907 908 /* Generate an fs event. */ 909 touch_file("watch_dir/file"); 910 911 uv_close((uv_handle_t*)&fs_event, close_cb); 912 913 uv_run(loop, UV_RUN_DEFAULT); 914 915 ASSERT(close_cb_called == 1); 916 917 /* Clean up */ 918 remove("watch_dir/file"); 919 remove("watch_dir/"); 920 921 MAKE_VALGRIND_HAPPY(); 922 return 0; 923 } 924 925 TEST_IMPL(fs_event_close_in_callback) { 926 #if defined(NO_FS_EVENTS) 927 RETURN_SKIP(NO_FS_EVENTS); 928 #elif defined(__MVS__) 929 RETURN_SKIP("Directory watching not supported on this platform."); 930 #endif 931 uv_loop_t* loop; 932 int r; 933 934 loop = uv_default_loop(); 935 936 fs_event_unlink_files(NULL); 937 create_dir("watch_dir"); 938 939 r = uv_fs_event_init(loop, &fs_event); 940 ASSERT(r == 0); 941 r = uv_fs_event_start(&fs_event, fs_event_cb_close, "watch_dir", 0); 942 ASSERT(r == 0); 943 944 r = uv_timer_init(loop, &timer); 945 ASSERT(r == 0); 946 r = uv_timer_start(&timer, fs_event_create_files, 100, 0); 947 ASSERT(r == 0); 948 949 uv_run(loop, UV_RUN_DEFAULT); 950 951 uv_close((uv_handle_t*)&timer, close_cb); 952 953 uv_run(loop, UV_RUN_ONCE); 954 955 ASSERT(close_cb_called == 2); 956 ASSERT(fs_event_cb_called == 3); 957 958 /* Clean up */ 959 fs_event_unlink_files(NULL); 960 remove("watch_dir/"); 961 962 MAKE_VALGRIND_HAPPY(); 963 return 0; 964 } 965 966 TEST_IMPL(fs_event_start_and_close) { 967 #if defined(NO_FS_EVENTS) 968 RETURN_SKIP(NO_FS_EVENTS); 969 #endif 970 uv_loop_t* loop; 971 uv_fs_event_t fs_event1; 972 uv_fs_event_t fs_event2; 973 int r; 974 975 loop = uv_default_loop(); 976 977 create_dir("watch_dir"); 978 979 r = uv_fs_event_init(loop, &fs_event1); 980 ASSERT(r == 0); 981 r = uv_fs_event_start(&fs_event1, fs_event_cb_dir, "watch_dir", 0); 982 ASSERT(r == 0); 983 984 r = uv_fs_event_init(loop, &fs_event2); 985 ASSERT(r == 0); 986 r = uv_fs_event_start(&fs_event2, fs_event_cb_dir, "watch_dir", 0); 987 ASSERT(r == 0); 988 989 uv_close((uv_handle_t*) &fs_event2, close_cb); 990 uv_close((uv_handle_t*) &fs_event1, close_cb); 991 992 uv_run(loop, UV_RUN_DEFAULT); 993 994 ASSERT(close_cb_called == 2); 995 996 remove("watch_dir/"); 997 MAKE_VALGRIND_HAPPY(); 998 return 0; 999 } 1000 1001 TEST_IMPL(fs_event_getpath) { 1002 #if defined(NO_FS_EVENTS) 1003 RETURN_SKIP(NO_FS_EVENTS); 1004 #endif 1005 uv_loop_t* loop = uv_default_loop(); 1006 unsigned i; 1007 int r; 1008 char buf[1024]; 1009 size_t len; 1010 const char* const watch_dir[] = { 1011 "watch_dir", 1012 "watch_dir/", 1013 "watch_dir///", 1014 "watch_dir/subfolder/..", 1015 "watch_dir//subfolder//..//", 1016 }; 1017 1018 create_dir("watch_dir"); 1019 create_dir("watch_dir/subfolder"); 1020 1021 1022 for (i = 0; i < ARRAY_SIZE(watch_dir); i++) { 1023 r = uv_fs_event_init(loop, &fs_event); 1024 ASSERT(r == 0); 1025 len = sizeof buf; 1026 r = uv_fs_event_getpath(&fs_event, buf, &len); 1027 ASSERT(r == UV_EINVAL); 1028 r = uv_fs_event_start(&fs_event, fail_cb, watch_dir[i], 0); 1029 ASSERT(r == 0); 1030 len = 0; 1031 r = uv_fs_event_getpath(&fs_event, buf, &len); 1032 ASSERT(r == UV_ENOBUFS); 1033 ASSERT(len < sizeof buf); /* sanity check */ 1034 ASSERT(len == strlen(watch_dir[i]) + 1); 1035 r = uv_fs_event_getpath(&fs_event, buf, &len); 1036 ASSERT(r == 0); 1037 ASSERT(len == strlen(watch_dir[i])); 1038 ASSERT(strcmp(buf, watch_dir[i]) == 0); 1039 r = uv_fs_event_stop(&fs_event); 1040 ASSERT(r == 0); 1041 uv_close((uv_handle_t*) &fs_event, close_cb); 1042 1043 uv_run(loop, UV_RUN_DEFAULT); 1044 1045 ASSERT(close_cb_called == 1); 1046 close_cb_called = 0; 1047 } 1048 1049 remove("watch_dir/"); 1050 MAKE_VALGRIND_HAPPY(); 1051 return 0; 1052 } 1053 1054 #if defined(__APPLE__) 1055 1056 static int fs_event_error_reported; 1057 1058 static void fs_event_error_report_cb(uv_fs_event_t* handle, 1059 const char* filename, 1060 int events, 1061 int status) { 1062 if (status != 0) 1063 fs_event_error_reported = status; 1064 } 1065 1066 static void timer_cb_nop(uv_timer_t* handle) { 1067 ++timer_cb_called; 1068 uv_close((uv_handle_t*) handle, close_cb); 1069 } 1070 1071 static void fs_event_error_report_close_cb(uv_handle_t* handle) { 1072 ASSERT(handle != NULL); 1073 close_cb_called++; 1074 1075 /* handle is allocated on-stack, no need to free it */ 1076 } 1077 1078 1079 TEST_IMPL(fs_event_error_reporting) { 1080 unsigned int i; 1081 uv_loop_t loops[1024]; 1082 uv_fs_event_t events[ARRAY_SIZE(loops)]; 1083 uv_loop_t* loop; 1084 uv_fs_event_t* event; 1085 1086 TEST_FILE_LIMIT(ARRAY_SIZE(loops) * 3); 1087 1088 remove("watch_dir/"); 1089 create_dir("watch_dir"); 1090 1091 /* Create a lot of loops, and start FSEventStream in each of them. 1092 * Eventually, this should create enough streams to make FSEventStreamStart() 1093 * fail. 1094 */ 1095 for (i = 0; i < ARRAY_SIZE(loops); i++) { 1096 loop = &loops[i]; 1097 ASSERT(0 == uv_loop_init(loop)); 1098 event = &events[i]; 1099 1100 timer_cb_called = 0; 1101 close_cb_called = 0; 1102 ASSERT(0 == uv_fs_event_init(loop, event)); 1103 ASSERT(0 == uv_fs_event_start(event, 1104 fs_event_error_report_cb, 1105 "watch_dir", 1106 0)); 1107 uv_unref((uv_handle_t*) event); 1108 1109 /* Let loop run for some time */ 1110 ASSERT(0 == uv_timer_init(loop, &timer)); 1111 ASSERT(0 == uv_timer_start(&timer, timer_cb_nop, 2, 0)); 1112 uv_run(loop, UV_RUN_DEFAULT); 1113 ASSERT(1 == timer_cb_called); 1114 ASSERT(1 == close_cb_called); 1115 if (fs_event_error_reported != 0) 1116 break; 1117 } 1118 1119 /* At least one loop should fail */ 1120 ASSERT(fs_event_error_reported == UV_EMFILE); 1121 1122 /* Stop and close all events, and destroy loops */ 1123 do { 1124 loop = &loops[i]; 1125 event = &events[i]; 1126 1127 ASSERT(0 == uv_fs_event_stop(event)); 1128 uv_ref((uv_handle_t*) event); 1129 uv_close((uv_handle_t*) event, fs_event_error_report_close_cb); 1130 1131 close_cb_called = 0; 1132 uv_run(loop, UV_RUN_DEFAULT); 1133 ASSERT(close_cb_called == 1); 1134 1135 uv_loop_close(loop); 1136 } while (i-- != 0); 1137 1138 remove("watch_dir/"); 1139 MAKE_VALGRIND_HAPPY(); 1140 return 0; 1141 } 1142 1143 #else /* !defined(__APPLE__) */ 1144 1145 TEST_IMPL(fs_event_error_reporting) { 1146 /* No-op, needed only for FSEvents backend */ 1147 1148 MAKE_VALGRIND_HAPPY(); 1149 return 0; 1150 } 1151 1152 #endif /* defined(__APPLE__) */ 1153 1154 TEST_IMPL(fs_event_watch_invalid_path) { 1155 #if defined(NO_FS_EVENTS) 1156 RETURN_SKIP(NO_FS_EVENTS); 1157 #endif 1158 1159 uv_loop_t* loop; 1160 int r; 1161 1162 loop = uv_default_loop(); 1163 r = uv_fs_event_init(loop, &fs_event); 1164 ASSERT(r == 0); 1165 r = uv_fs_event_start(&fs_event, fs_event_cb_file, "<:;", 0); 1166 ASSERT(r != 0); 1167 ASSERT(uv_is_active((uv_handle_t*) &fs_event) == 0); 1168 r = uv_fs_event_start(&fs_event, fs_event_cb_file, "", 0); 1169 ASSERT(r != 0); 1170 ASSERT(uv_is_active((uv_handle_t*) &fs_event) == 0); 1171 MAKE_VALGRIND_HAPPY(); 1172 return 0; 1173 } 1174