Rework file notifications, kqueue has problems with directory monitors
[emacs.git] / test / automated / file-notify-tests.el
blob7bacddd8855bd1a4742daee5b25f4c8ad6e9cdb0
1 ;;; file-notify-tests.el --- Tests of file notifications -*- lexical-binding: t; -*-
3 ;; Copyright (C) 2013-2015 Free Software Foundation, Inc.
5 ;; Author: Michael Albinus <michael.albinus@gmx.de>
7 ;; This program is free software: you can redistribute it and/or
8 ;; modify it under the terms of the GNU General Public License as
9 ;; published by the Free Software Foundation, either version 3 of the
10 ;; License, or (at your option) any later version.
12 ;; This program is distributed in the hope that it will be useful, but
13 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 ;; General Public License for more details.
17 ;; You should have received a copy of the GNU General Public License
18 ;; along with this program. If not, see `http://www.gnu.org/licenses/'.
20 ;;; Commentary:
22 ;; Some of the tests require access to a remote host files. Since
23 ;; this could be problematic, a mock-up connection method "mock" is
24 ;; used. Emulating a remote connection, it simply calls "sh -i".
25 ;; Tramp's file name handlers still run, so this test is sufficient
26 ;; except for connection establishing.
28 ;; If you want to test a real Tramp connection, set
29 ;; $REMOTE_TEMPORARY_FILE_DIRECTORY to a suitable value in order to
30 ;; overwrite the default value. If you want to skip tests accessing a
31 ;; remote host, set this environment variable to "/dev/null" or
32 ;; whatever is appropriate on your system.
34 ;; A whole test run can be performed calling the command `file-notify-test-all'.
36 ;;; Code:
38 (require 'ert)
39 (require 'filenotify)
40 (require 'tramp)
42 ;; There is no default value on w32 systems, which could work out of the box.
43 (defconst file-notify-test-remote-temporary-file-directory
44 (cond
45 ((getenv "REMOTE_TEMPORARY_FILE_DIRECTORY"))
46 ((eq system-type 'windows-nt) null-device)
47 (t (add-to-list
48 'tramp-methods
49 '("mock"
50 (tramp-login-program "sh")
51 (tramp-login-args (("-i")))
52 (tramp-remote-shell "/bin/sh")
53 (tramp-remote-shell-args ("-c"))
54 (tramp-connection-timeout 10)))
55 (format "/mock::%s" temporary-file-directory)))
56 "Temporary directory for Tramp tests.")
58 (defvar file-notify--test-tmpfile nil)
59 (defvar file-notify--test-tmpfile1 nil)
60 (defvar file-notify--test-desc nil)
61 (defvar file-notify--test-results nil)
62 (defvar file-notify--test-event nil)
63 (defvar file-notify--test-events nil)
64 (defvar file-notify--test-expected-events nil)
66 (defun file-notify--test-timeout ()
67 "Timeout to wait for arriving events, in seconds."
68 (if (file-remote-p temporary-file-directory) 6 3))
70 (defun file-notify--test-cleanup ()
71 "Cleanup after a test."
72 (file-notify-rm-watch file-notify--test-desc)
74 (when (and file-notify--test-tmpfile
75 (file-exists-p file-notify--test-tmpfile))
76 (if (file-directory-p file-notify--test-tmpfile)
77 (delete-directory file-notify--test-tmpfile 'recursive)
78 (delete-file file-notify--test-tmpfile)))
79 (when (and file-notify--test-tmpfile1
80 (file-exists-p file-notify--test-tmpfile1))
81 (if (file-directory-p file-notify--test-tmpfile1)
82 (delete-directory file-notify--test-tmpfile1 'recursive)
83 (delete-file file-notify--test-tmpfile1)))
84 (when (file-remote-p temporary-file-directory)
85 (tramp-cleanup-connection
86 (tramp-dissect-file-name temporary-file-directory) nil 'keep-password))
88 (setq file-notify--test-tmpfile nil
89 file-notify--test-tmpfile1 nil
90 file-notify--test-desc nil
91 file-notify--test-results nil
92 file-notify--test-events nil
93 file-notify--test-expected-events nil)
94 (when file-notify--test-event
95 (error "file-notify--test-event should not be set but bound dynamically")))
97 (setq password-cache-expiry nil
98 tramp-verbose 0
99 tramp-message-show-message nil)
101 ;; This shall happen on hydra only.
102 (when (getenv "NIX_STORE")
103 (add-to-list 'tramp-remote-path 'tramp-own-remote-path))
105 ;; We do not want to try and fail `file-notify-add-watch'.
106 (defun file-notify--test-local-enabled ()
107 "Whether local file notification is enabled.
108 This is needed for local `temporary-file-directory' only, in the
109 remote case we return always t."
110 (or file-notify--library
111 (file-remote-p temporary-file-directory)))
113 (defvar file-notify--test-remote-enabled-checked nil
114 "Cached result of `file-notify--test-remote-enabled'.
115 If the function did run, the value is a cons cell, the `cdr'
116 being the result.")
118 (defun file-notify--test-remote-enabled ()
119 "Whether remote file notification is enabled."
120 (unless (consp file-notify--test-remote-enabled-checked)
121 (let (desc)
122 (ignore-errors
123 (and
124 (file-remote-p file-notify-test-remote-temporary-file-directory)
125 (file-directory-p file-notify-test-remote-temporary-file-directory)
126 (file-writable-p file-notify-test-remote-temporary-file-directory)
127 (setq desc
128 (file-notify-add-watch
129 file-notify-test-remote-temporary-file-directory
130 '(change) 'ignore))))
131 (setq file-notify--test-remote-enabled-checked (cons t desc))
132 (when desc (file-notify-rm-watch desc))))
133 ;; Return result.
134 (cdr file-notify--test-remote-enabled-checked))
136 (defun file-notify--test-library ()
137 "The used libray for the test, as string.
138 In the remote case, it is the process name which runs on the
139 remote host, or nil."
140 (if (null (file-remote-p temporary-file-directory))
141 (symbol-name file-notify--library)
142 (and (consp file-notify--test-remote-enabled-checked)
143 (processp (cdr file-notify--test-remote-enabled-checked))
144 (replace-regexp-in-string
145 "<[[:digit:]]+>\\'" ""
146 (process-name (cdr file-notify--test-remote-enabled-checked))))))
148 (defmacro file-notify--deftest-remote (test docstring)
149 "Define ert `TEST-remote' for remote files."
150 (declare (indent 1))
151 `(ert-deftest ,(intern (concat (symbol-name test) "-remote")) ()
152 ,docstring
153 (let* ((temporary-file-directory
154 file-notify-test-remote-temporary-file-directory)
155 (ert-test (ert-get-test ',test)))
156 (skip-unless (file-notify--test-remote-enabled))
157 (tramp-cleanup-connection
158 (tramp-dissect-file-name temporary-file-directory) nil 'keep-password)
159 (funcall (ert-test-body ert-test)))))
161 (ert-deftest file-notify-test00-availability ()
162 "Test availability of `file-notify'."
163 (skip-unless (file-notify--test-local-enabled))
164 ;; Report the native library which has been used.
165 (message "Library: `%s'" (file-notify--test-library))
166 (should
167 (setq file-notify--test-desc
168 (file-notify-add-watch temporary-file-directory '(change) 'ignore)))
170 ;; Cleanup.
171 (file-notify--test-cleanup))
173 (file-notify--deftest-remote file-notify-test00-availability
174 "Test availability of `file-notify' for remote files.")
176 (ert-deftest file-notify-test01-add-watch ()
177 "Check `file-notify-add-watch'."
178 (skip-unless (file-notify--test-local-enabled))
180 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
181 file-notify--test-tmpfile1
182 (format "%s/%s" file-notify--test-tmpfile (md5 (current-time-string))))
184 ;; Check, that different valid parameters are accepted.
185 (should
186 (setq file-notify--test-desc
187 (file-notify-add-watch temporary-file-directory '(change) 'ignore)))
188 (file-notify-rm-watch file-notify--test-desc)
189 (should
190 (setq file-notify--test-desc
191 (file-notify-add-watch
192 temporary-file-directory '(attribute-change) 'ignore)))
193 (file-notify-rm-watch file-notify--test-desc)
194 (should
195 (setq file-notify--test-desc
196 (file-notify-add-watch
197 temporary-file-directory '(change attribute-change) 'ignore)))
198 (file-notify-rm-watch file-notify--test-desc)
199 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
200 (should
201 (setq file-notify--test-desc
202 (file-notify-add-watch
203 file-notify--test-tmpfile '(change attribute-change) 'ignore)))
204 (file-notify-rm-watch file-notify--test-desc)
205 (delete-file file-notify--test-tmpfile)
207 ;; Check error handling.
208 (should-error (file-notify-add-watch 1 2 3 4)
209 :type 'wrong-number-of-arguments)
210 (should
211 (equal (should-error
212 (file-notify-add-watch 1 2 3))
213 '(wrong-type-argument 1)))
214 (should
215 (equal (should-error
216 (file-notify-add-watch temporary-file-directory 2 3))
217 '(wrong-type-argument 2)))
218 (should
219 (equal (should-error
220 (file-notify-add-watch temporary-file-directory '(change) 3))
221 '(wrong-type-argument 3)))
222 ;; The upper directory of a file must exist.
223 (should
224 (equal (should-error
225 (file-notify-add-watch
226 file-notify--test-tmpfile1 '(change attribute-change) 'ignore))
227 `(file-notify-error
228 "Directory does not exist" ,file-notify--test-tmpfile)))
230 ;; Cleanup.
231 (file-notify--test-cleanup))
233 (file-notify--deftest-remote file-notify-test01-add-watch
234 "Check `file-notify-add-watch' for remote files.")
236 (defun file-notify--test-event-test ()
237 "Ert test function to be called by `file-notify--test-event-handler'.
238 We cannot pass arguments, so we assume that `file-notify--test-event'
239 is bound somewhere."
240 ;; Check the descriptor.
241 (should (equal (car file-notify--test-event) file-notify--test-desc))
242 ;; Check the file name.
243 (should
244 (or (string-equal (file-notify--event-file-name file-notify--test-event)
245 file-notify--test-tmpfile)
246 (string-equal (file-notify--event-file-name file-notify--test-event)
247 file-notify--test-tmpfile1)
248 (string-equal (file-notify--event-file-name file-notify--test-event)
249 temporary-file-directory)))
250 ;; Check the second file name if exists.
251 (when (eq (nth 1 file-notify--test-event) 'renamed)
252 (should
253 (or (string-equal (file-notify--event-file1-name file-notify--test-event)
254 file-notify--test-tmpfile1)
255 (string-equal (file-notify--event-file1-name file-notify--test-event)
256 temporary-file-directory)))))
258 (defun file-notify--test-event-handler (event)
259 "Run a test over FILE-NOTIFY--TEST-EVENT.
260 For later analysis, append the test result to `file-notify--test-results'
261 and the event to `file-notify--test-events'."
262 (let* ((file-notify--test-event event)
263 (result
264 (ert-run-test (make-ert-test :body 'file-notify--test-event-test))))
265 ;; Do not add temporary files, this would confuse the checks.
266 (unless (string-match
267 (regexp-quote ".#")
268 (file-notify--event-file-name file-notify--test-event))
269 ;;(message "file-notify--test-event-handler %S" file-notify--test-event)
270 (setq file-notify--test-events
271 (append file-notify--test-events `(,file-notify--test-event))
272 file-notify--test-results
273 (append file-notify--test-results `(,result))))))
275 (defun file-notify--test-make-temp-name ()
276 "Create a temporary file name for test."
277 (expand-file-name
278 (make-temp-name "file-notify-test") temporary-file-directory))
280 (defmacro file-notify--wait-for-events (timeout until)
281 "Wait for and return file notification events until form UNTIL is true.
282 TIMEOUT is the maximum time to wait for, in seconds."
283 `(with-timeout (,timeout (ignore))
284 (while (null ,until)
285 (read-event nil nil 0.1))))
287 (defmacro file-notify--test-with-events (events &rest body)
288 "Run BODY collecting events and then compare with EVENTS.
289 Don't wait longer than timeout seconds for the events to be delivered."
290 (declare (indent 1))
291 (let ((outer (make-symbol "outer")))
292 `(let ((,outer file-notify--test-events))
293 (setq file-notify--test-expected-events
294 (append file-notify--test-expected-events ,events))
295 (let (file-notify--test-events)
296 ,@body
297 (file-notify--wait-for-events
298 ;; More events need more time. Use some fudge factor.
299 (* (ceiling (length ,events) 100) (file-notify--test-timeout))
300 (= (length ,events) (length file-notify--test-events)))
301 (should (equal ,events (mapcar #'cadr file-notify--test-events)))
302 (setq ,outer (append ,outer file-notify--test-events)))
303 (setq file-notify--test-events ,outer))))
305 (ert-deftest file-notify-test02-events ()
306 "Check file creation/change/removal notifications."
307 (skip-unless (file-notify--test-local-enabled))
308 ;; Under cygwin there are so bad timings that it doesn't make sense to test.
309 (skip-unless (not (eq system-type 'cygwin)))
311 (unwind-protect
312 (progn
313 ;; Check file change and deletion.
314 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
315 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
316 (should
317 (setq file-notify--test-desc
318 (file-notify-add-watch
319 file-notify--test-tmpfile
320 '(change) 'file-notify--test-event-handler)))
321 (file-notify--test-with-events '(changed deleted)
322 (write-region
323 "another text" nil file-notify--test-tmpfile nil 'no-message)
324 (delete-file file-notify--test-tmpfile))
325 ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it.
326 (let (file-notify--test-events)
327 (file-notify-rm-watch file-notify--test-desc))
329 ;; Check file creation, change and deletion when watching a
330 ;; directory. There must be a `stopped' event when deleting
331 ;; the directory. It doesn't work for w32notify.
332 (unless (string-equal (file-notify--test-library) "w32notify")
333 (let ((temporary-file-directory
334 (make-temp-file "file-notify-test-parent" t)))
335 (should
336 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
337 file-notify--test-desc
338 (file-notify-add-watch
339 temporary-file-directory
340 '(change) 'file-notify--test-event-handler)))
341 (file-notify--test-with-events
342 ;; There are two `deleted' events, for the file and
343 ;; for the directory. Except for kqueue.
344 (if (string-equal (file-notify--test-library) "kqueue")
345 '(created changed deleted stopped)
346 '(created changed deleted deleted stopped))
347 (write-region
348 "any text" nil file-notify--test-tmpfile nil 'no-message)
349 (read-event nil nil 0.1)
350 (delete-directory temporary-file-directory 'recursive))
351 ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it.
352 (let (file-notify--test-events)
353 (file-notify-rm-watch file-notify--test-desc))))
355 ;; Check copy of files inside a directory.
356 (let ((temporary-file-directory
357 (make-temp-file "file-notify-test-parent" t)))
358 (should
359 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
360 file-notify--test-tmpfile1 (file-notify--test-make-temp-name)
361 file-notify--test-desc
362 (file-notify-add-watch
363 temporary-file-directory
364 '(change) 'file-notify--test-event-handler)))
365 (file-notify--test-with-events
366 ;; w32notify does not distinguish between `changed' and
367 ;; `attribute-changed'.
368 (if (string-equal (file-notify--test-library) "w32notify")
369 '(created changed changed deleted)
370 '(created changed created changed deleted stopped))
371 (write-region
372 "any text" nil file-notify--test-tmpfile nil 'no-message)
373 (read-event nil nil 0.1)
374 (copy-file file-notify--test-tmpfile file-notify--test-tmpfile1)
375 ;; The next two events shall not be visible.
376 (read-event nil nil 0.1)
377 (set-file-modes file-notify--test-tmpfile 000)
378 (read-event nil nil 0.1)
379 (set-file-times file-notify--test-tmpfile '(0 0))
380 (read-event nil nil 0.1)
381 (delete-directory temporary-file-directory 'recursive))
382 ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it.
383 (let (file-notify--test-events)
384 (file-notify-rm-watch file-notify--test-desc)))
386 ;; Check rename of files inside a directory.
387 (let ((temporary-file-directory
388 (make-temp-file "file-notify-test-parent" t)))
389 (should
390 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
391 file-notify--test-tmpfile1 (file-notify--test-make-temp-name)
392 file-notify--test-desc
393 (file-notify-add-watch
394 temporary-file-directory
395 '(change) 'file-notify--test-event-handler)))
396 (file-notify--test-with-events '(created changed renamed)
397 (write-region
398 "any text" nil file-notify--test-tmpfile nil 'no-message)
399 (read-event nil nil 0.1)
400 (rename-file file-notify--test-tmpfile file-notify--test-tmpfile1)
401 ;; After the rename, we won't get events anymore.
402 (read-event nil nil 0.1)
403 (delete-directory temporary-file-directory 'recursive))
404 ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it.
405 (let (file-notify--test-events)
406 (file-notify-rm-watch file-notify--test-desc)))
408 ;; Check attribute change. It doesn't work for kqueue and w32notify.
409 (unless (or (string-equal (file-notify--test-library) "kqueue")
410 (string-equal (file-notify--test-library) "w32notify"))
411 (should
412 (setq file-notify--test-desc
413 (file-notify-add-watch
414 file-notify--test-tmpfile
415 '(attribute-change) 'file-notify--test-event-handler)))
416 (file-notify--test-with-events
417 (if (file-remote-p temporary-file-directory)
418 ;; In the remote case, `write-region' raises also an
419 ;; `attribute-changed' event.
420 '(attribute-changed attribute-changed attribute-changed)
421 '(attribute-changed attribute-changed))
422 ;; We must use short delays between the operations.
423 ;; Otherwise, not all events arrive us in the remote case.
424 (write-region
425 "any text" nil file-notify--test-tmpfile nil 'no-message)
426 (read-event nil nil 0.1)
427 (set-file-modes file-notify--test-tmpfile 000)
428 (read-event nil nil 0.1)
429 (set-file-times file-notify--test-tmpfile '(0 0))
430 (read-event nil nil 0.1)
431 (delete-file file-notify--test-tmpfile))
432 ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it.
433 (let (file-notify--test-events)
434 (file-notify-rm-watch file-notify--test-desc)))
436 ;; Check the global sequence again just to make sure that
437 ;; `file-notify--test-events' has been set correctly.
438 (should (equal (mapcar #'cadr file-notify--test-events)
439 file-notify--test-expected-events))
440 (should file-notify--test-results)
441 (dolist (result file-notify--test-results)
442 (when (ert-test-failed-p result)
443 (ert-fail
444 (cadr (ert-test-result-with-condition-condition result))))))
446 ;; Cleanup.
447 (file-notify--test-cleanup)))
449 (file-notify--deftest-remote file-notify-test02-events
450 "Check file creation/change/removal notifications for remote files.")
452 (require 'autorevert)
453 (setq auto-revert-notify-exclude-dir-regexp "nothing-to-be-excluded"
454 auto-revert-remote-files t
455 auto-revert-stop-on-user-input nil)
457 (ert-deftest file-notify-test03-autorevert ()
458 "Check autorevert via file notification."
459 (skip-unless (file-notify--test-local-enabled))
460 ;; `auto-revert-buffers' runs every 5". And we must wait, until the
461 ;; file has been reverted.
462 (let ((timeout (if (file-remote-p temporary-file-directory) 60 10))
463 buf)
464 (unwind-protect
465 (progn
466 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
468 (write-region
469 "any text" nil file-notify--test-tmpfile nil 'no-message)
470 (setq buf (find-file-noselect file-notify--test-tmpfile))
471 (with-current-buffer buf
472 (should (string-equal (buffer-string) "any text"))
473 ;; `buffer-stale--default-function' checks for
474 ;; `verify-visited-file-modtime'. We must ensure that it
475 ;; returns nil.
476 (sleep-for 1)
477 (auto-revert-mode 1)
479 ;; `auto-revert-buffers' runs every 5".
480 (with-timeout (timeout (ignore))
481 (while (null auto-revert-notify-watch-descriptor)
482 (sleep-for 1)))
484 ;; Check, that file notification has been used.
485 (should auto-revert-mode)
486 (should auto-revert-use-notify)
487 (should auto-revert-notify-watch-descriptor)
489 ;; Modify file. We wait for a second, in order to have
490 ;; another timestamp.
491 (with-current-buffer (get-buffer-create "*Messages*")
492 (narrow-to-region (point-max) (point-max)))
493 (sleep-for 1)
494 (write-region
495 "another text" nil file-notify--test-tmpfile nil 'no-message)
497 ;; Check, that the buffer has been reverted.
498 (with-current-buffer (get-buffer-create "*Messages*")
499 (file-notify--wait-for-events
500 timeout
501 (string-match
502 (format-message "Reverting buffer `%s'." (buffer-name buf))
503 (buffer-string))))
504 (should (string-match "another text" (buffer-string)))
506 ;; Stop file notification. Autorevert shall still work via polling.
507 (file-notify-rm-watch auto-revert-notify-watch-descriptor)
508 (file-notify--wait-for-events
509 timeout (null auto-revert-use-notify))
510 (should-not auto-revert-use-notify)
511 (should-not auto-revert-notify-watch-descriptor)
513 ;; Modify file. We wait for two seconds, in order to have
514 ;; another timestamp. One second seems to be too short.
515 (with-current-buffer (get-buffer-create "*Messages*")
516 (narrow-to-region (point-max) (point-max)))
517 (sleep-for 2)
518 (write-region
519 "foo bla" nil file-notify--test-tmpfile nil 'no-message)
521 ;; Check, that the buffer has been reverted.
522 (with-current-buffer (get-buffer-create "*Messages*")
523 (file-notify--wait-for-events
524 timeout
525 (string-match
526 (format-message "Reverting buffer `%s'." (buffer-name buf))
527 (buffer-string))))
528 (should (string-match "foo bla" (buffer-string)))))
530 ;; Cleanup.
531 (with-current-buffer "*Messages*" (widen))
532 (ignore-errors (kill-buffer buf))
533 (file-notify--test-cleanup))))
535 (file-notify--deftest-remote file-notify-test03-autorevert
536 "Check autorevert via file notification for remote files.")
538 (ert-deftest file-notify-test04-file-validity ()
539 "Check `file-notify-valid-p' for files."
540 (skip-unless (file-notify--test-local-enabled))
541 ;; Under cygwin there are so bad timings that it doesn't make sense to test.
542 (skip-unless (not (eq system-type 'cygwin)))
544 (unwind-protect
545 (progn
546 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
547 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
548 (should
549 (setq file-notify--test-desc
550 (file-notify-add-watch
551 file-notify--test-tmpfile
552 '(change) #'file-notify--test-event-handler)))
553 (should (file-notify-valid-p file-notify--test-desc))
554 ;; After calling `file-notify-rm-watch', the descriptor is not
555 ;; valid anymore.
556 (file-notify-rm-watch file-notify--test-desc)
557 (should-not (file-notify-valid-p file-notify--test-desc))
558 (delete-file file-notify--test-tmpfile))
560 ;; Cleanup.
561 (file-notify--test-cleanup))
563 (unwind-protect
564 (progn
565 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
566 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
567 (should
568 (setq file-notify--test-desc
569 (file-notify-add-watch
570 file-notify--test-tmpfile
571 '(change) #'file-notify--test-event-handler)))
572 (file-notify--test-with-events '(changed deleted)
573 (should (file-notify-valid-p file-notify--test-desc))
574 (write-region
575 "another text" nil file-notify--test-tmpfile nil 'no-message)
576 (read-event nil nil 0.1)
577 (delete-file file-notify--test-tmpfile))
578 ;; After deleting the file, the descriptor is not valid anymore.
579 (should-not (file-notify-valid-p file-notify--test-desc))
580 (file-notify-rm-watch file-notify--test-desc))
582 ;; Cleanup.
583 (file-notify--test-cleanup))
585 (unwind-protect
586 ;; The batch-mode operation of w32notify is fragile (there's no
587 ;; input threads to send the message to).
588 (unless (string-equal (file-notify--test-library) "w32notify")
589 (let ((temporary-file-directory
590 (make-temp-file "file-notify-test-parent" t)))
591 (should
592 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
593 file-notify--test-desc
594 (file-notify-add-watch
595 temporary-file-directory
596 '(change) #'file-notify--test-event-handler)))
597 (file-notify--test-with-events
598 ;; There are two `deleted' events, for the file and for
599 ;; the directory. Except for kqueue.
600 (if (string-equal (file-notify--test-library) "kqueue")
601 '(created changed deleted stopped)
602 '(created changed deleted deleted stopped))
603 (should (file-notify-valid-p file-notify--test-desc))
604 (write-region
605 "any text" nil file-notify--test-tmpfile nil 'no-message)
606 (read-event nil nil 0.1)
607 (delete-directory temporary-file-directory t))
608 ;; After deleting the parent directory, the descriptor must
609 ;; not be valid anymore.
610 (should-not (file-notify-valid-p file-notify--test-desc))))
612 ;; Cleanup.
613 (file-notify--test-cleanup)))
615 (file-notify--deftest-remote file-notify-test04-file-validity
616 "Check `file-notify-valid-p' via file notification for remote files.")
618 (ert-deftest file-notify-test05-dir-validity ()
619 "Check `file-notify-valid-p' for directories."
620 (skip-unless (file-notify--test-local-enabled))
622 (unwind-protect
623 (progn
624 (setq file-notify--test-tmpfile
625 (file-name-as-directory (file-notify--test-make-temp-name)))
626 (make-directory file-notify--test-tmpfile)
627 (should
628 (setq file-notify--test-desc
629 (file-notify-add-watch
630 file-notify--test-tmpfile
631 '(change) #'file-notify--test-event-handler)))
632 (should (file-notify-valid-p file-notify--test-desc))
633 ;; After removing the watch, the descriptor must not be valid
634 ;; anymore.
635 (file-notify-rm-watch file-notify--test-desc)
636 (file-notify--wait-for-events
637 (file-notify--test-timeout)
638 (not (file-notify-valid-p file-notify--test-desc)))
639 (should-not (file-notify-valid-p file-notify--test-desc)))
641 ;; Cleanup.
642 (file-notify--test-cleanup))
644 (unwind-protect
645 ;; The batch-mode operation of w32notify is fragile (there's no
646 ;; input threads to send the message to).
647 (unless (and noninteractive
648 (string-equal (file-notify--test-library) "w32notify"))
649 (setq file-notify--test-tmpfile
650 (file-name-as-directory (file-notify--test-make-temp-name)))
651 (make-directory file-notify--test-tmpfile)
652 (should
653 (setq file-notify--test-desc
654 (file-notify-add-watch
655 file-notify--test-tmpfile
656 '(change) #'file-notify--test-event-handler)))
657 (should (file-notify-valid-p file-notify--test-desc))
658 ;; After deleting the directory, the descriptor must not be
659 ;; valid anymore.
660 (delete-directory file-notify--test-tmpfile t)
661 (file-notify--wait-for-events
662 (file-notify--test-timeout)
663 (not (file-notify-valid-p file-notify--test-desc)))
664 (should-not (file-notify-valid-p file-notify--test-desc)))
666 ;; Cleanup.
667 (file-notify--test-cleanup)))
669 (file-notify--deftest-remote file-notify-test05-dir-validity
670 "Check `file-notify-valid-p' via file notification for remote directories.")
672 (ert-deftest file-notify-test06-many-events ()
673 "Check that events are not dropped."
674 (skip-unless (file-notify--test-local-enabled))
675 ;; Under cygwin there are so bad timings that it doesn't make sense to test.
676 (skip-unless (not (eq system-type 'cygwin)))
677 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
678 (make-directory file-notify--test-tmpfile)
679 (should
680 (setq file-notify--test-desc
681 (file-notify-add-watch
682 file-notify--test-tmpfile
683 '(change) 'file-notify--test-event-handler)))
684 (unwind-protect
685 (let ((n 1000)
686 source-file-list target-file-list
687 (default-directory file-notify--test-tmpfile))
688 (dotimes (i n)
689 ;; It matters which direction we rename, at least for
690 ;; kqueue. This backend parses directories in alphabetic
691 ;; order (x%d before y%d). So we rename both directions.
692 (if (zerop (mod i 2))
693 (progn
694 (push (expand-file-name (format "x%d" i)) source-file-list)
695 (push (expand-file-name (format "y%d" i)) target-file-list))
696 (push (expand-file-name (format "y%d" i)) source-file-list)
697 (push (expand-file-name (format "x%d" i)) target-file-list)))
698 (file-notify--test-with-events (make-list (+ n n) 'created)
699 (let ((source-file-list source-file-list)
700 (target-file-list target-file-list))
701 (while (and source-file-list target-file-list)
702 (write-region "" nil (pop source-file-list) nil 'no-message)
703 (read-event nil nil 0.1)
704 (write-region "" nil (pop target-file-list) nil 'no-message))))
705 (file-notify--test-with-events (make-list n 'renamed)
706 (let ((source-file-list source-file-list)
707 (target-file-list target-file-list))
708 (while (and source-file-list target-file-list)
709 (rename-file (pop source-file-list) (pop target-file-list) t))))
710 (file-notify--test-with-events (make-list n 'deleted)
711 (dolist (file target-file-list)
712 (delete-file file))))
713 (file-notify--test-cleanup)))
715 (file-notify--deftest-remote file-notify-test06-many-events
716 "Check that events are not dropped for remote directories.")
718 (defun file-notify-test-all (&optional interactive)
719 "Run all tests for \\[file-notify]."
720 (interactive "p")
721 (if interactive
722 (ert-run-tests-interactively "^file-notify-")
723 (ert-run-tests-batch "^file-notify-")))
725 ;; TODO:
727 ;; * For w32notify, no stopped events arrive when a directory is removed.
728 ;; * Try to handle arriving events under cygwin reliably.
730 (provide 'file-notify-tests)
731 ;;; file-notify-tests.el ends here