1 ;;; file-notify-tests.el --- Tests of file notifications -*- lexical-binding: t; -*-
3 ;; Copyright (C) 2013-2016 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/'.
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'.
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
45 ((getenv "REMOTE_TEMPORARY_FILE_DIRECTORY"))
46 ((eq system-type 'windows-nt) null-device)
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-desc1 nil)
62 (defvar file-notify--test-desc2 nil)
63 (defvar file-notify--test-results nil)
64 (defvar file-notify--test-event nil)
65 (defvar file-notify--test-events nil)
67 (defconst file-notify--test-read-event-timeout 0.01
68 "Timeout for `read-event' calls.
69 It is different for local and remote file notification libraries.")
71 (defun file-notify--test-timeout ()
72 "Timeout to wait for arriving events, in seconds."
74 ((file-remote-p temporary-file-directory) 6)
75 ((string-equal (file-notify--test-library) "w32notify") 4)
76 ((eq system-type 'cygwin) 6)
79 (defmacro file-notify--wait-for-events (timeout until)
80 "Wait for and return file notification events until form UNTIL is true.
81 TIMEOUT is the maximum time to wait for, in seconds."
82 `(with-timeout (,timeout (ignore))
84 (read-event nil nil file-notify--test-read-event-timeout))))
86 (defun file-notify--test-no-descriptors ()
87 "Check that `file-notify-descriptors' is an empty hash table.
88 Return nil when any other file notification watch is still active."
89 ;; Give read events a last chance.
90 (file-notify--wait-for-events
91 (file-notify--test-timeout)
92 (zerop (hash-table-count file-notify-descriptors)))
94 (zerop (hash-table-count file-notify-descriptors)))
96 (defun file-notify--test-no-descriptors-explainer ()
97 "Explain why `file-notify--test-no-descriptors' fails."
98 (let ((result (list "Watch descriptor(s) existent:")))
100 (lambda (key value) (push (cons key value) result))
101 file-notify-descriptors)
104 (put 'file-notify--test-no-descriptors 'ert-explainer
105 'file-notify--test-no-descriptors-explainer)
107 (defun file-notify--test-cleanup-p ()
108 "Check, that the test has cleaned up the environment as much as needed."
109 ;; `file-notify--test-event' should not be set but bound
111 (should-not file-notify--test-event)
112 ;; The test should have cleaned up this already. Let's check
114 (should (file-notify--test-no-descriptors)))
116 (defun file-notify--test-cleanup ()
117 "Cleanup after a test."
118 (file-notify-rm-watch file-notify--test-desc)
119 (file-notify-rm-watch file-notify--test-desc1)
120 (file-notify-rm-watch file-notify--test-desc2)
123 (delete-file (file-newest-backup file-notify--test-tmpfile)))
125 (if (file-directory-p file-notify--test-tmpfile)
126 (delete-directory file-notify--test-tmpfile 'recursive)
127 (delete-file file-notify--test-tmpfile)))
129 (if (file-directory-p file-notify--test-tmpfile1)
130 (delete-directory file-notify--test-tmpfile1 'recursive)
131 (delete-file file-notify--test-tmpfile1)))
133 (when (file-remote-p temporary-file-directory)
134 (tramp-cleanup-connection
135 (tramp-dissect-file-name temporary-file-directory) nil 'keep-password)))
137 (setq file-notify--test-tmpfile nil
138 file-notify--test-tmpfile1 nil
139 file-notify--test-desc nil
140 file-notify--test-desc1 nil
141 file-notify--test-desc2 nil
142 file-notify--test-results nil
143 file-notify--test-events nil))
145 (setq password-cache-expiry nil
147 tramp-message-show-message nil)
149 ;; This shall happen on hydra only.
150 (when (getenv "NIX_STORE")
151 (add-to-list 'tramp-remote-path 'tramp-own-remote-path))
153 ;; We do not want to try and fail `file-notify-add-watch'.
154 (defun file-notify--test-local-enabled ()
155 "Whether local file notification is enabled.
156 This is needed for local `temporary-file-directory' only, in the
157 remote case we return always t."
158 (or file-notify--library
159 (file-remote-p temporary-file-directory)))
161 (defvar file-notify--test-remote-enabled-checked nil
162 "Cached result of `file-notify--test-remote-enabled'.
163 If the function did run, the value is a cons cell, the `cdr'
166 (defun file-notify--test-remote-enabled ()
167 "Whether remote file notification is enabled."
168 (unless (consp file-notify--test-remote-enabled-checked)
172 (file-remote-p file-notify-test-remote-temporary-file-directory)
173 (file-directory-p file-notify-test-remote-temporary-file-directory)
174 (file-writable-p file-notify-test-remote-temporary-file-directory)
176 (file-notify-add-watch
177 file-notify-test-remote-temporary-file-directory
178 '(change) #'ignore))))
179 (setq file-notify--test-remote-enabled-checked (cons t desc))
180 (when desc (file-notify-rm-watch desc))))
182 (cdr file-notify--test-remote-enabled-checked))
184 (defun file-notify--test-library ()
185 "The used library for the test, as a string.
186 In the remote case, it is the process name which runs on the
187 remote host, or nil."
188 (if (null (file-remote-p temporary-file-directory))
189 (symbol-name file-notify--library)
190 (and (consp file-notify--test-remote-enabled-checked)
191 (processp (cdr file-notify--test-remote-enabled-checked))
192 (replace-regexp-in-string
193 "<[[:digit:]]+>\\'" ""
194 (process-name (cdr file-notify--test-remote-enabled-checked))))))
196 (defmacro file-notify--deftest-remote (test docstring)
197 "Define ert `TEST-remote' for remote files."
199 `(ert-deftest ,(intern (concat (symbol-name test) "-remote")) ()
201 :tags '(:expensive-test)
202 (let* ((temporary-file-directory
203 file-notify-test-remote-temporary-file-directory)
204 (file-notify--test-read-event-timeout 0.1)
205 (ert-test (ert-get-test ',test)))
206 (skip-unless (file-notify--test-remote-enabled))
207 (tramp-cleanup-connection
208 (tramp-dissect-file-name temporary-file-directory) nil 'keep-password)
209 (funcall (ert-test-body ert-test)))))
211 (ert-deftest file-notify-test00-availability ()
212 "Test availability of `file-notify'."
213 (skip-unless (file-notify--test-local-enabled))
217 ;; Report the native library which has been used.
218 (message "Library: `%s'" (file-notify--test-library))
220 (setq file-notify--test-desc
221 (file-notify-add-watch
222 temporary-file-directory '(change) #'ignore)))
223 (file-notify-rm-watch file-notify--test-desc)
225 ;; The environment shall be cleaned up.
226 (file-notify--test-cleanup-p))
229 (file-notify--test-cleanup)))
231 (file-notify--deftest-remote file-notify-test00-availability
232 "Test availability of `file-notify' for remote files.")
234 (ert-deftest file-notify-test01-add-watch ()
235 "Check `file-notify-add-watch'."
236 (skip-unless (file-notify--test-local-enabled))
240 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
241 file-notify--test-tmpfile1
243 "%s/%s" file-notify--test-tmpfile (md5 (current-time-string))))
245 ;; Check, that different valid parameters are accepted.
247 (setq file-notify--test-desc
248 (file-notify-add-watch
249 temporary-file-directory '(change) #'ignore)))
250 (file-notify-rm-watch file-notify--test-desc)
252 (setq file-notify--test-desc
253 (file-notify-add-watch
254 temporary-file-directory '(attribute-change) #'ignore)))
255 (file-notify-rm-watch file-notify--test-desc)
257 (setq file-notify--test-desc
258 (file-notify-add-watch
259 temporary-file-directory '(change attribute-change) #'ignore)))
260 (file-notify-rm-watch file-notify--test-desc)
261 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
263 (setq file-notify--test-desc
264 (file-notify-add-watch
265 file-notify--test-tmpfile '(change attribute-change) #'ignore)))
266 (file-notify-rm-watch file-notify--test-desc)
267 (delete-file file-notify--test-tmpfile)
269 ;; Check error handling.
270 (should-error (file-notify-add-watch 1 2 3 4)
271 :type 'wrong-number-of-arguments)
274 (file-notify-add-watch 1 2 3))
275 '(wrong-type-argument 1)))
278 (file-notify-add-watch temporary-file-directory 2 3))
279 '(wrong-type-argument 2)))
282 (file-notify-add-watch temporary-file-directory '(change) 3))
283 '(wrong-type-argument 3)))
284 ;; The upper directory of a file must exist.
287 (file-notify-add-watch
288 file-notify--test-tmpfile1
289 '(change attribute-change) #'ignore))
291 "Directory does not exist" ,file-notify--test-tmpfile)))
293 ;; The environment shall be cleaned up.
294 (file-notify--test-cleanup-p))
297 (file-notify--test-cleanup)))
299 (file-notify--deftest-remote file-notify-test01-add-watch
300 "Check `file-notify-add-watch' for remote files.")
302 (defun file-notify--test-event-test ()
303 "Ert test function to be called by `file-notify--test-event-handler'.
304 We cannot pass arguments, so we assume that `file-notify--test-event'
306 ;; Check the descriptor.
307 (should (equal (car file-notify--test-event) file-notify--test-desc))
308 ;; Check the file name.
311 (file-notify--event-watched-file file-notify--test-event)
312 (file-notify--event-file-name file-notify--test-event)))
313 ;; Check the second file name if exists.
314 (when (eq (nth 1 file-notify--test-event) 'renamed)
317 (file-notify--event-watched-file file-notify--test-event)
318 (file-notify--event-file1-name file-notify--test-event)))))
320 (defun file-notify--test-event-handler (event)
321 "Run a test over FILE-NOTIFY--TEST-EVENT.
322 For later analysis, append the test result to `file-notify--test-results'
323 and the event to `file-notify--test-events'."
324 (let* ((file-notify--test-event event)
326 (ert-run-test (make-ert-test :body 'file-notify--test-event-test))))
327 ;; Do not add lock files, this would confuse the checks.
328 (unless (string-match
330 (file-notify--event-file-name file-notify--test-event))
331 ;;(message "file-notify--test-event-handler result: %s event: %S"
332 ;;(null (ert-test-failed-p result)) file-notify--test-event)
333 (setq file-notify--test-events
334 (append file-notify--test-events `(,file-notify--test-event))
335 file-notify--test-results
336 (append file-notify--test-results `(,result))))))
338 (defun file-notify--test-make-temp-name ()
339 "Create a temporary file name for test."
341 (make-temp-name "file-notify-test") temporary-file-directory))
343 (defun file-notify--test-with-events-check (events)
344 "Check whether received events match one of the EVENTS alternatives."
346 (dolist (elt events result)
349 (if (eq (car elt) :random)
350 (equal (sort (cdr elt) 'string-lessp)
351 (sort (mapcar #'cadr file-notify--test-events)
353 (equal elt (mapcar #'cadr file-notify--test-events))))))))
355 (defun file-notify--test-with-events-explainer (events)
356 "Explain why `file-notify--test-with-events-check' fails."
357 (if (null (cdr events))
358 (format "Received events do not match expected events\n%s\n%s"
359 (mapcar #'cadr file-notify--test-events) (car events))
361 "Received events do not match any sequence of expected events\n%s\n%s"
362 (mapcar #'cadr file-notify--test-events) events)))
364 (put 'file-notify--test-with-events-check 'ert-explainer
365 'file-notify--test-with-events-explainer)
367 (defmacro file-notify--test-with-events (events &rest body)
368 "Run BODY collecting events and then compare with EVENTS.
369 EVENTS is either a simple list of events, or a list of lists of
370 events, which represent different possible results. The first
371 event of a list could be the pseudo event `:random', which is
372 just an indicator for comparison.
374 Don't wait longer than timeout seconds for the events to be
377 `(let* ((events (if (consp (car ,events)) ,events (list ,events)))
382 (lambda (x) (length (if (eq (car x) :random) (cdr x) x)))
385 ;; Flush pending events.
386 (file-notify--wait-for-events
387 (file-notify--test-timeout)
389 (setq file-notify--test-events nil
390 file-notify--test-results nil)
392 (file-notify--wait-for-events
393 ;; More events need more time. Use some fudge factor.
394 (* (ceiling max-length 100) (file-notify--test-timeout))
395 (= max-length (length file-notify--test-events)))
396 ;; Check the result sequence just to make sure that all events
398 (dolist (result file-notify--test-results)
399 (when (ert-test-failed-p result)
401 (cadr (ert-test-result-with-condition-condition result)))))
402 ;; One of the possible event sequences shall match.
403 (should (file-notify--test-with-events-check events))))
405 (ert-deftest file-notify-test02-events ()
406 "Check file creation/change/removal notifications."
407 (skip-unless (file-notify--test-local-enabled))
411 ;; Check file creation, change and deletion. It doesn't work
412 ;; for cygwin and kqueue, because we don't use an implicit
413 ;; directory monitor (kqueue), or the timings are too bad (cygwin).
414 (unless (or (eq system-type 'cygwin)
415 (string-equal (file-notify--test-library) "kqueue"))
416 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
418 (setq file-notify--test-desc
419 (file-notify-add-watch
420 file-notify--test-tmpfile
421 '(change) #'file-notify--test-event-handler)))
422 (file-notify--test-with-events
424 ;; cygwin recognizes only `deleted' and `stopped' events.
425 ((eq system-type 'cygwin)
427 (t '(created changed deleted stopped)))
429 "another text" nil file-notify--test-tmpfile nil 'no-message)
430 (read-event nil nil file-notify--test-read-event-timeout)
431 (delete-file file-notify--test-tmpfile))
432 (file-notify-rm-watch file-notify--test-desc))
434 ;; Check file change and deletion.
435 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
436 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
438 (setq file-notify--test-desc
439 (file-notify-add-watch
440 file-notify--test-tmpfile
441 '(change) #'file-notify--test-event-handler)))
442 (file-notify--test-with-events
444 ;; cygwin recognizes only `deleted' and `stopped' events.
445 ((eq system-type 'cygwin)
447 ;; inotify and kqueue raise just one `changed' event.
448 ((or (string-equal "inotify" (file-notify--test-library))
449 (string-equal "kqueue" (file-notify--test-library)))
450 '(changed deleted stopped))
451 ;; gfilenotify raises one or two `changed' events
452 ;; randomly, no chance to test. So we accept both cases.
453 ((string-equal "gfilenotify" (file-notify--test-library))
454 '((changed deleted stopped)
455 (changed changed deleted stopped)))
456 (t '(changed changed deleted stopped)))
458 "another text" nil file-notify--test-tmpfile nil 'no-message)
459 (read-event nil nil file-notify--test-read-event-timeout)
460 (delete-file file-notify--test-tmpfile))
461 (file-notify-rm-watch file-notify--test-desc)
463 ;; Check file creation, change and deletion when watching a
464 ;; directory. There must be a `stopped' event when deleting
466 (let ((temporary-file-directory
467 (make-temp-file "file-notify-test-parent" t)))
469 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
470 file-notify--test-desc
471 (file-notify-add-watch
472 temporary-file-directory
473 '(change) #'file-notify--test-event-handler)))
474 (file-notify--test-with-events
476 ;; w32notify does not raise `deleted' and `stopped'
477 ;; events for the watched directory.
478 ((string-equal (file-notify--test-library) "w32notify")
479 '(created changed deleted))
480 ;; cygwin recognizes only `deleted' and `stopped' events.
481 ((eq system-type 'cygwin)
483 ;; There are two `deleted' events, for the file and for
484 ;; the directory. Except for kqueue.
485 ((string-equal (file-notify--test-library) "kqueue")
486 '(created changed deleted stopped))
487 (t '(created changed deleted deleted stopped)))
489 "any text" nil file-notify--test-tmpfile nil 'no-message)
490 (read-event nil nil file-notify--test-read-event-timeout)
491 (delete-directory temporary-file-directory 'recursive))
492 (file-notify-rm-watch file-notify--test-desc))
494 ;; Check copy of files inside a directory.
495 (let ((temporary-file-directory
496 (make-temp-file "file-notify-test-parent" t)))
498 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
499 file-notify--test-tmpfile1 (file-notify--test-make-temp-name)
500 file-notify--test-desc
501 (file-notify-add-watch
502 temporary-file-directory
503 '(change) #'file-notify--test-event-handler)))
504 (file-notify--test-with-events
506 ;; w32notify does not distinguish between `changed' and
507 ;; `attribute-changed'. It does not raise `deleted'
508 ;; and `stopped' events for the watched directory.
509 ((string-equal (file-notify--test-library) "w32notify")
510 '(created changed created changed
511 changed changed changed
513 ;; cygwin recognizes only `deleted' and `stopped' events.
514 ((eq system-type 'cygwin)
516 ;; There are three `deleted' events, for two files and
517 ;; for the directory. Except for kqueue.
518 ((string-equal (file-notify--test-library) "kqueue")
519 '(created changed created changed deleted stopped))
520 (t '(created changed created changed
521 deleted deleted deleted stopped)))
523 "any text" nil file-notify--test-tmpfile nil 'no-message)
524 (read-event nil nil file-notify--test-read-event-timeout)
525 (copy-file file-notify--test-tmpfile file-notify--test-tmpfile1)
526 ;; The next two events shall not be visible.
527 (read-event nil nil file-notify--test-read-event-timeout)
528 (set-file-modes file-notify--test-tmpfile 000)
529 (read-event nil nil file-notify--test-read-event-timeout)
530 (set-file-times file-notify--test-tmpfile '(0 0))
531 (read-event nil nil file-notify--test-read-event-timeout)
532 (delete-directory temporary-file-directory 'recursive))
533 (file-notify-rm-watch file-notify--test-desc))
535 ;; Check rename of files inside a directory.
536 (let ((temporary-file-directory
537 (make-temp-file "file-notify-test-parent" t)))
539 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
540 file-notify--test-tmpfile1 (file-notify--test-make-temp-name)
541 file-notify--test-desc
542 (file-notify-add-watch
543 temporary-file-directory
544 '(change) #'file-notify--test-event-handler)))
545 (file-notify--test-with-events
547 ;; w32notify does not raise `deleted' and `stopped'
548 ;; events for the watched directory.
549 ((string-equal (file-notify--test-library) "w32notify")
550 '(created changed renamed deleted))
551 ;; cygwin recognizes only `deleted' and `stopped' events.
552 ((eq system-type 'cygwin)
554 ;; There are two `deleted' events, for the file and for
555 ;; the directory. Except for kqueue.
556 ((string-equal (file-notify--test-library) "kqueue")
557 '(created changed renamed deleted stopped))
558 (t '(created changed renamed deleted deleted stopped)))
560 "any text" nil file-notify--test-tmpfile nil 'no-message)
561 (read-event nil nil file-notify--test-read-event-timeout)
562 (rename-file file-notify--test-tmpfile file-notify--test-tmpfile1)
563 ;; After the rename, we won't get events anymore.
564 (read-event nil nil file-notify--test-read-event-timeout)
565 (delete-directory temporary-file-directory 'recursive))
566 (file-notify-rm-watch file-notify--test-desc))
568 ;; Check attribute change. Does not work for cygwin.
569 (unless (eq system-type 'cygwin)
570 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
572 "any text" nil file-notify--test-tmpfile nil 'no-message)
574 (setq file-notify--test-desc
575 (file-notify-add-watch
576 file-notify--test-tmpfile
577 '(attribute-change) #'file-notify--test-event-handler)))
578 (file-notify--test-with-events
580 ;; w32notify does not distinguish between `changed' and
581 ;; `attribute-changed'. Under MS Windows 7, we get
582 ;; four `changed' events, and under MS Windows 10 just
584 ((string-equal (file-notify--test-library) "w32notify")
586 (changed changed changed changed)))
587 ;; For kqueue and in the remote case, `write-region'
588 ;; raises also an `attribute-changed' event.
589 ((or (string-equal (file-notify--test-library) "kqueue")
590 (file-remote-p temporary-file-directory))
591 '(attribute-changed attribute-changed attribute-changed))
592 (t '(attribute-changed attribute-changed)))
594 "any text" nil file-notify--test-tmpfile nil 'no-message)
595 (read-event nil nil file-notify--test-read-event-timeout)
596 (set-file-modes file-notify--test-tmpfile 000)
597 (read-event nil nil file-notify--test-read-event-timeout)
598 (set-file-times file-notify--test-tmpfile '(0 0))
599 (read-event nil nil file-notify--test-read-event-timeout)
600 (delete-file file-notify--test-tmpfile))
601 (file-notify-rm-watch file-notify--test-desc))
603 ;; The environment shall be cleaned up.
604 (file-notify--test-cleanup-p))
607 (file-notify--test-cleanup)))
609 (file-notify--deftest-remote file-notify-test02-events
610 "Check file creation/change/removal notifications for remote files.")
612 (require 'autorevert)
613 (setq auto-revert-notify-exclude-dir-regexp "nothing-to-be-excluded"
614 auto-revert-remote-files t
615 auto-revert-stop-on-user-input nil)
617 (ert-deftest file-notify-test03-autorevert ()
618 "Check autorevert via file notification."
619 (skip-unless (file-notify--test-local-enabled))
621 ;; `auto-revert-buffers' runs every 5". And we must wait, until the
622 ;; file has been reverted.
623 (let ((timeout (if (file-remote-p temporary-file-directory) 60 10))
627 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
629 "any text" nil file-notify--test-tmpfile nil 'no-message)
630 (setq buf (find-file-noselect file-notify--test-tmpfile))
631 (with-current-buffer buf
632 (should (string-equal (buffer-string) "any text"))
633 ;; `buffer-stale--default-function' checks for
634 ;; `verify-visited-file-modtime'. We must ensure that it
639 ;; `auto-revert-buffers' runs every 5".
640 (with-timeout (timeout (ignore))
641 (while (null auto-revert-notify-watch-descriptor)
644 ;; Check, that file notification has been used.
645 (should auto-revert-mode)
646 (should auto-revert-use-notify)
647 (should auto-revert-notify-watch-descriptor)
649 ;; Modify file. We wait for a second, in order to have
650 ;; another timestamp.
651 (with-current-buffer (get-buffer-create "*Messages*")
652 (narrow-to-region (point-max) (point-max)))
655 "another text" nil file-notify--test-tmpfile nil 'no-message)
657 ;; Check, that the buffer has been reverted.
658 (with-current-buffer (get-buffer-create "*Messages*")
659 (file-notify--wait-for-events
662 (format-message "Reverting buffer `%s'." (buffer-name buf))
664 (should (string-match "another text" (buffer-string)))
666 ;; Stop file notification. Autorevert shall still work via polling.
667 (file-notify-rm-watch auto-revert-notify-watch-descriptor)
668 (file-notify--wait-for-events
669 timeout (null auto-revert-use-notify))
670 (should-not auto-revert-use-notify)
671 (should-not auto-revert-notify-watch-descriptor)
673 ;; Modify file. We wait for two seconds, in order to
674 ;; have another timestamp. One second seems to be too
676 (with-current-buffer (get-buffer-create "*Messages*")
677 (narrow-to-region (point-max) (point-max)))
680 "foo bla" nil file-notify--test-tmpfile nil 'no-message)
682 ;; Check, that the buffer has been reverted.
683 (with-current-buffer (get-buffer-create "*Messages*")
684 (file-notify--wait-for-events
687 (format-message "Reverting buffer `%s'." (buffer-name buf))
689 (should (string-match "foo bla" (buffer-string))))
691 ;; The environment shall be cleaned up.
692 (file-notify--test-cleanup-p))
695 (with-current-buffer "*Messages*" (widen))
696 (ignore-errors (kill-buffer buf))
697 (file-notify--test-cleanup))))
699 (file-notify--deftest-remote file-notify-test03-autorevert
700 "Check autorevert via file notification for remote files.")
702 (ert-deftest file-notify-test04-file-validity ()
703 "Check `file-notify-valid-p' for files."
704 (skip-unless (file-notify--test-local-enabled))
708 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
709 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
711 (setq file-notify--test-desc
712 (file-notify-add-watch
713 file-notify--test-tmpfile '(change) #'ignore)))
714 (should (file-notify-valid-p file-notify--test-desc))
715 ;; After calling `file-notify-rm-watch', the descriptor is not
717 (file-notify-rm-watch file-notify--test-desc)
718 (should-not (file-notify-valid-p file-notify--test-desc))
719 (delete-file file-notify--test-tmpfile)
721 ;; The environment shall be cleaned up.
722 (file-notify--test-cleanup-p))
725 (file-notify--test-cleanup))
729 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
730 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
732 (setq file-notify--test-desc
733 (file-notify-add-watch
734 file-notify--test-tmpfile
735 '(change) #'file-notify--test-event-handler)))
736 (should (file-notify-valid-p file-notify--test-desc))
737 (file-notify--test-with-events
739 ;; cygwin recognizes only `deleted' and `stopped' events.
740 ((eq system-type 'cygwin)
742 ;; inotify and kqueue raise just one `changed' event.
743 ((or (string-equal "inotify" (file-notify--test-library))
744 (string-equal "kqueue" (file-notify--test-library)))
745 '(changed deleted stopped))
746 ;; gfilenotify raises one or two `changed' events
747 ;; randomly, no chance to test. So we accept both cases.
748 ((string-equal "gfilenotify" (file-notify--test-library))
749 '((changed deleted stopped)
750 (changed changed deleted stopped)))
751 (t '(changed changed deleted stopped)))
753 "another text" nil file-notify--test-tmpfile nil 'no-message)
754 (read-event nil nil file-notify--test-read-event-timeout)
755 (delete-file file-notify--test-tmpfile))
756 ;; After deleting the file, the descriptor is not valid anymore.
757 (should-not (file-notify-valid-p file-notify--test-desc))
758 (file-notify-rm-watch file-notify--test-desc)
760 ;; The environment shall be cleaned up.
761 (file-notify--test-cleanup-p))
764 (file-notify--test-cleanup))
767 (let ((temporary-file-directory
768 (make-temp-file "file-notify-test-parent" t)))
770 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
771 file-notify--test-desc
772 (file-notify-add-watch
773 temporary-file-directory
774 '(change) #'file-notify--test-event-handler)))
775 (should (file-notify-valid-p file-notify--test-desc))
776 (file-notify--test-with-events
778 ;; w32notify does not raise `deleted' and `stopped' events
779 ;; for the watched directory.
780 ((string-equal (file-notify--test-library) "w32notify")
781 '(created changed deleted))
782 ;; cygwin recognizes only `deleted' and `stopped' events.
783 ((eq system-type 'cygwin)
785 ;; There are two `deleted' events, for the file and for the
786 ;; directory. Except for kqueue.
787 ((string-equal (file-notify--test-library) "kqueue")
788 '(created changed deleted stopped))
789 (t '(created changed deleted deleted stopped)))
791 "any text" nil file-notify--test-tmpfile nil 'no-message)
792 (read-event nil nil file-notify--test-read-event-timeout)
793 (delete-directory temporary-file-directory t))
794 ;; After deleting the parent directory, the descriptor must
795 ;; not be valid anymore.
796 (should-not (file-notify-valid-p file-notify--test-desc))
798 ;; The environment shall be cleaned up.
799 (file-notify--test-cleanup-p))
802 (file-notify--test-cleanup)))
804 (file-notify--deftest-remote file-notify-test04-file-validity
805 "Check `file-notify-valid-p' via file notification for remote files.")
807 (ert-deftest file-notify-test05-dir-validity ()
808 "Check `file-notify-valid-p' for directories."
809 (skip-unless (file-notify--test-local-enabled))
814 (setq file-notify--test-tmpfile
815 (make-temp-file "file-notify-test-parent" t)))
817 (setq file-notify--test-desc
818 (file-notify-add-watch
819 file-notify--test-tmpfile '(change) #'ignore)))
820 (should (file-notify-valid-p file-notify--test-desc))
821 ;; After removing the watch, the descriptor must not be valid
823 (file-notify-rm-watch file-notify--test-desc)
824 (file-notify--wait-for-events
825 (file-notify--test-timeout)
826 (not (file-notify-valid-p file-notify--test-desc)))
827 (should-not (file-notify-valid-p file-notify--test-desc))
828 (delete-directory file-notify--test-tmpfile t)
830 ;; The environment shall be cleaned up.
831 (file-notify--test-cleanup-p))
834 (file-notify--test-cleanup))
839 (setq file-notify--test-tmpfile
840 (make-temp-file "file-notify-test-parent" t)))
842 (setq file-notify--test-desc
843 (file-notify-add-watch
844 file-notify--test-tmpfile '(change) #'ignore)))
845 (should (file-notify-valid-p file-notify--test-desc))
846 ;; After deleting the directory, the descriptor must not be
848 (delete-directory file-notify--test-tmpfile t)
849 (file-notify--wait-for-events
850 (file-notify--test-timeout)
851 (not (file-notify-valid-p file-notify--test-desc)))
852 (should-not (file-notify-valid-p file-notify--test-desc))
854 ;; The environment shall be cleaned up.
855 (file-notify--test-cleanup-p))
858 (file-notify--test-cleanup)))
860 (file-notify--deftest-remote file-notify-test05-dir-validity
861 "Check `file-notify-valid-p' via file notification for remote directories.")
863 (ert-deftest file-notify-test06-many-events ()
864 "Check that events are not dropped."
865 :tags '(:expensive-test)
866 (skip-unless (file-notify--test-local-enabled))
869 (setq file-notify--test-tmpfile
870 (make-temp-file "file-notify-test-parent" t)))
872 (setq file-notify--test-desc
873 (file-notify-add-watch
874 file-notify--test-tmpfile
875 '(change) #'file-notify--test-event-handler)))
878 source-file-list target-file-list
879 (default-directory file-notify--test-tmpfile))
881 ;; It matters which direction we rename, at least for
882 ;; kqueue. This backend parses directories in alphabetic
883 ;; order (x%d before y%d). So we rename into both directions.
884 (if (zerop (mod i 2))
886 (push (expand-file-name (format "x%d" i)) source-file-list)
887 (push (expand-file-name (format "y%d" i)) target-file-list))
888 (push (expand-file-name (format "y%d" i)) source-file-list)
889 (push (expand-file-name (format "x%d" i)) target-file-list)))
890 (file-notify--test-with-events (make-list (+ n n) 'created)
891 (let ((source-file-list source-file-list)
892 (target-file-list target-file-list))
893 (while (and source-file-list target-file-list)
894 (read-event nil nil file-notify--test-read-event-timeout)
895 (write-region "" nil (pop source-file-list) nil 'no-message)
896 (read-event nil nil file-notify--test-read-event-timeout)
897 (write-region "" nil (pop target-file-list) nil 'no-message))))
898 (file-notify--test-with-events
900 ;; w32notify fires both `deleted' and `renamed' events.
901 ((string-equal (file-notify--test-library) "w32notify")
904 (setq r (append '(deleted renamed) r)))))
905 ;; cygwin fires `changed' and `deleted' events, sometimes
907 ((eq system-type 'cygwin)
908 (let ((r '(:random)))
910 (setq r (append r '(changed deleted))))))
911 (t (make-list n 'renamed)))
912 (let ((source-file-list source-file-list)
913 (target-file-list target-file-list))
914 (while (and source-file-list target-file-list)
915 (read-event nil nil file-notify--test-read-event-timeout)
916 (rename-file (pop source-file-list) (pop target-file-list) t))))
917 (file-notify--test-with-events (make-list n 'deleted)
918 (dolist (file target-file-list)
919 (read-event nil nil file-notify--test-read-event-timeout)
920 (delete-file file) file-notify--test-read-event-timeout))
921 (delete-directory file-notify--test-tmpfile)
923 ;; The environment shall be cleaned up.
924 (file-notify--test-cleanup-p))
927 (file-notify--test-cleanup)))
929 (file-notify--deftest-remote file-notify-test06-many-events
930 "Check that events are not dropped for remote directories.")
932 (ert-deftest file-notify-test07-backup ()
933 "Check that backup keeps file notification."
934 (skip-unless (file-notify--test-local-enabled))
938 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
939 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
941 (setq file-notify--test-desc
942 (file-notify-add-watch
943 file-notify--test-tmpfile
944 '(change) #'file-notify--test-event-handler)))
945 (should (file-notify-valid-p file-notify--test-desc))
946 (file-notify--test-with-events
948 ;; On Cygwin there is one `changed' event in both the
949 ;; local and remote cases.
950 ((eq system-type 'cygwin) '(changed))
951 ;; For w32notify and in the remote case, there are two
953 ((or (string-equal (file-notify--test-library) "w32notify")
954 (file-remote-p temporary-file-directory))
956 ;; gfilenotify raises one or two `changed' events
957 ;; randomly, no chance to test. So we accept both cases.
958 ((string-equal "gfilenotify" (file-notify--test-library))
962 ;; There shouldn't be any problem, because the file is kept.
964 (let ((buffer-file-name file-notify--test-tmpfile)
965 (make-backup-files t)
966 (backup-by-copying t)
967 (kept-new-versions 1)
968 (delete-old-versions t))
969 (insert "another text")
971 ;; After saving the buffer, the descriptor is still valid.
972 (should (file-notify-valid-p file-notify--test-desc))
973 (delete-file file-notify--test-tmpfile)
975 ;; The environment shall be cleaned up.
976 (file-notify--test-cleanup-p))
979 (file-notify--test-cleanup))
982 ;; It doesn't work for kqueue, because we don't use an implicit
983 ;; directory monitor.
984 (unless (string-equal (file-notify--test-library) "kqueue")
985 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
987 "any text" nil file-notify--test-tmpfile nil 'no-message)
989 (setq file-notify--test-desc
990 (file-notify-add-watch
991 file-notify--test-tmpfile
992 '(change) #'file-notify--test-event-handler)))
993 (should (file-notify-valid-p file-notify--test-desc))
994 (file-notify--test-with-events
996 ;; On Cygwin we only get the `changed' event.
997 ((eq system-type 'cygwin) '(changed))
998 (t '(renamed created changed)))
999 ;; The file is renamed when creating a backup. It shall
1000 ;; still be watched.
1002 (let ((buffer-file-name file-notify--test-tmpfile)
1003 (make-backup-files t)
1004 (backup-by-copying nil)
1005 (backup-by-copying-when-mismatch nil)
1006 (kept-new-versions 1)
1007 (delete-old-versions t))
1008 (insert "another text")
1010 ;; After saving the buffer, the descriptor is still valid.
1011 (should (file-notify-valid-p file-notify--test-desc))
1012 (delete-file file-notify--test-tmpfile)
1014 ;; The environment shall be cleaned up.
1015 (file-notify--test-cleanup-p))
1018 (file-notify--test-cleanup)))
1020 (file-notify--deftest-remote file-notify-test07-backup
1021 "Check that backup keeps file notification for remote files.")
1023 (ert-deftest file-notify-test08-watched-file-in-watched-dir ()
1024 "Watches a directory and a file in that directory separately.
1025 Checks that the callbacks are only called with events with
1026 descriptors that were issued when registering the watches. This
1027 test caters for the situation in bug#22736 where the callback for
1028 the directory received events for the file with the descriptor of
1030 :tags '(:expensive-test)
1031 (skip-unless (file-notify--test-local-enabled))
1033 ;; A directory to be watched.
1035 (setq file-notify--test-tmpfile
1036 (make-temp-file "file-notify-test-parent" t)))
1037 ;; A file to be watched.
1039 (setq file-notify--test-tmpfile1
1040 (let ((temporary-file-directory file-notify--test-tmpfile))
1041 (file-notify--test-make-temp-name))))
1042 (write-region "any text" nil file-notify--test-tmpfile1 nil 'no-message)
1044 (cl-flet (;; Directory monitor.
1045 (dir-callback (event)
1046 (let ((file-notify--test-desc file-notify--test-desc1))
1047 (file-notify--test-event-handler event)))
1049 (file-callback (event)
1050 (let ((file-notify--test-desc file-notify--test-desc2))
1051 (file-notify--test-event-handler event))))
1053 (setq file-notify--test-desc1
1054 (file-notify-add-watch
1055 file-notify--test-tmpfile
1056 '(change) #'dir-callback)))
1058 (setq file-notify--test-desc2
1059 (file-notify-add-watch
1060 file-notify--test-tmpfile1
1061 '(change) #'file-callback)))
1062 (should (file-notify-valid-p file-notify--test-desc1))
1063 (should (file-notify-valid-p file-notify--test-desc2))
1064 (should-not (equal file-notify--test-desc1 file-notify--test-desc2))
1065 ;; gfilenotify raises one or two `changed' events randomly in
1066 ;; the file monitor, no chance to test.
1067 (unless (string-equal "gfilenotify" (file-notify--test-library))
1068 (let ((n 100) events)
1069 ;; Compute the expected events.
1070 (dotimes (_i (/ n 2))
1074 ;; Directory monitor and file monitor.
1076 ;; In the remote case, there are two `changed'
1078 ((file-remote-p temporary-file-directory)
1079 '(changed changed changed changed))
1080 ;; The directory monitor in kqueue does not
1081 ;; raise any `changed' event. Just the file
1082 ;; monitor event is received.
1083 ((string-equal (file-notify--test-library) "kqueue")
1085 ;; Otherwise, both monitors report the
1087 (t '(changed changed)))
1089 ;; Just the directory monitor.
1091 ;; In kqueue, there is an additional `changed'
1093 ((string-equal (file-notify--test-library) "kqueue")
1094 '(changed created changed))
1095 (t '(created changed))))
1097 ;; gvfs-monitor-dir returns the events in random order.
1098 (when (string-equal "gvfs-monitor-dir" (file-notify--test-library))
1099 (setq events (cons :random events)))
1102 (file-notify--test-with-events events
1104 (read-event nil nil file-notify--test-read-event-timeout)
1105 (if (zerop (mod i 2))
1107 "any text" nil file-notify--test-tmpfile1 t 'no-message)
1108 (let ((temporary-file-directory file-notify--test-tmpfile))
1111 (file-notify--test-make-temp-name) nil 'no-message)))))))
1113 ;; If we delete the file, the directory monitor shall still be
1114 ;; active. We receive the `deleted' event from both the
1115 ;; directory and the file monitor. The `stopped' event is
1116 ;; from the file monitor. It's undecided in which order the
1117 ;; the directory and the file monitor are triggered.
1118 (file-notify--test-with-events '(:random deleted deleted stopped)
1119 (delete-file file-notify--test-tmpfile1))
1120 (should (file-notify-valid-p file-notify--test-desc1))
1121 (should-not (file-notify-valid-p file-notify--test-desc2))
1123 ;; Now we delete the directory.
1124 (file-notify--test-with-events
1126 ;; In kqueue, just one `deleted' event for the directory
1128 ((string-equal (file-notify--test-library) "kqueue")
1131 ;; The directory monitor raises a `deleted' event for
1132 ;; every file contained in the directory, we must
1137 file-notify--test-tmpfile nil
1138 directory-files-no-dot-files-regexp 'nosort))
1140 ;; The events of the directory itself.
1142 ;; w32notify does not raise `deleted' and `stopped'
1143 ;; events for the watched directory.
1144 ((string-equal (file-notify--test-library) "w32notify") '())
1145 (t '(deleted stopped))))))
1146 (delete-directory file-notify--test-tmpfile 'recursive))
1147 (should-not (file-notify-valid-p file-notify--test-desc1))
1148 (should-not (file-notify-valid-p file-notify--test-desc2))
1150 ;; The environment shall be cleaned up.
1151 (file-notify--test-cleanup-p))
1154 (file-notify--test-cleanup)))
1156 (file-notify--deftest-remote file-notify-test08-watched-file-in-watched-dir
1157 "Check `file-notify-test08-watched-file-in-watched-dir' for remote files.")
1159 (ert-deftest file-notify-test09-sufficient-resources ()
1160 "Check that file notification does not use too many resources."
1161 :tags '(:expensive-test)
1162 (skip-unless (file-notify--test-local-enabled))
1163 ;; This test is intended for kqueue only.
1164 (skip-unless (string-equal (file-notify--test-library) "kqueue"))
1167 (setq file-notify--test-tmpfile
1168 (make-temp-file "file-notify-test-parent" t)))
1170 (let ((temporary-file-directory file-notify--test-tmpfile)
1174 ;; We watch directories, because we want to reach the upper
1175 ;; limit. Watching a file might not be sufficient, because
1176 ;; most of the libraries implement this as watching the
1178 (setq file-notify--test-tmpfile1
1179 (make-temp-file "file-notify-test-parent" t)
1183 (file-notify-add-watch
1184 file-notify--test-tmpfile1 '(change) #'ignore))
1186 :type 'file-notify-error)
1187 ;; Remove watches. If we don't do it prior removing
1188 ;; directories, Emacs crashes in batch mode.
1189 (dolist (desc descs)
1190 (file-notify-rm-watch desc))
1191 ;; Remove directories.
1192 (delete-directory file-notify--test-tmpfile 'recursive)
1194 ;; The environment shall be cleaned up.
1195 (file-notify--test-cleanup-p))
1198 (file-notify--test-cleanup)))
1200 (file-notify--deftest-remote file-notify-test09-sufficient-resources
1201 "Check `file-notify-test09-sufficient-resources' for remote files.")
1203 (defun file-notify-test-all (&optional interactive)
1204 "Run all tests for \\[file-notify]."
1207 (ert-run-tests-interactively "^file-notify-")
1208 (ert-run-tests-batch "^file-notify-")))
1212 ;; * kqueue does not send all expected `deleted' events. Maybe due to
1213 ;; the missing directory monitor.
1214 ;; * For w32notify, no `deleted' and `stopped' events arrive when a
1215 ;; directory is removed.
1216 ;; * For w32notify, no `attribute-changed' events arrive. Its sends
1217 ;; `changed' events instead.
1218 ;; * Check, why cygwin recognizes only `deleted' and `stopped' events.
1220 (provide 'file-notify-tests)
1221 ;;; file-notify-tests.el ends here