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