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