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_NOT_NULL(handle); 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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