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