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