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
) 10)
79 (defun file-notify--test-cleanup ()
80 "Cleanup after a test."
81 (file-notify-rm-watch file-notify--test-desc
)
82 (file-notify-rm-watch file-notify--test-desc1
)
83 (file-notify-rm-watch file-notify--test-desc2
)
86 (delete-file (file-newest-backup file-notify--test-tmpfile
)))
88 (if (file-directory-p file-notify--test-tmpfile
)
89 (delete-directory file-notify--test-tmpfile
'recursive
)
90 (delete-file file-notify--test-tmpfile
)))
92 (if (file-directory-p file-notify--test-tmpfile1
)
93 (delete-directory file-notify--test-tmpfile1
'recursive
)
94 (delete-file file-notify--test-tmpfile1
)))
96 (when (file-remote-p temporary-file-directory
)
97 (tramp-cleanup-connection
98 (tramp-dissect-file-name temporary-file-directory
) nil
'keep-password
)))
100 (setq file-notify--test-tmpfile nil
101 file-notify--test-tmpfile1 nil
102 file-notify--test-desc nil
103 file-notify--test-desc1 nil
104 file-notify--test-desc2 nil
105 file-notify--test-results nil
106 file-notify--test-events nil
)
107 (when file-notify--test-event
108 (error "file-notify--test-event should not be set but bound dynamically")))
110 (setq password-cache-expiry nil
112 tramp-message-show-message nil
)
114 ;; This shall happen on hydra only.
115 (when (getenv "NIX_STORE")
116 (add-to-list 'tramp-remote-path
'tramp-own-remote-path
))
118 ;; We do not want to try and fail `file-notify-add-watch'.
119 (defun file-notify--test-local-enabled ()
120 "Whether local file notification is enabled.
121 This is needed for local `temporary-file-directory' only, in the
122 remote case we return always t."
123 (or file-notify--library
124 (file-remote-p temporary-file-directory
)))
126 (defvar file-notify--test-remote-enabled-checked nil
127 "Cached result of `file-notify--test-remote-enabled'.
128 If the function did run, the value is a cons cell, the `cdr'
131 (defun file-notify--test-remote-enabled ()
132 "Whether remote file notification is enabled."
133 (unless (consp file-notify--test-remote-enabled-checked
)
137 (file-remote-p file-notify-test-remote-temporary-file-directory
)
138 (file-directory-p file-notify-test-remote-temporary-file-directory
)
139 (file-writable-p file-notify-test-remote-temporary-file-directory
)
141 (file-notify-add-watch
142 file-notify-test-remote-temporary-file-directory
143 '(change) 'ignore
))))
144 (setq file-notify--test-remote-enabled-checked
(cons t desc
))
145 (when desc
(file-notify-rm-watch desc
))))
147 (cdr file-notify--test-remote-enabled-checked
))
149 (defun file-notify--test-library ()
150 "The used library for the test, as a string.
151 In the remote case, it is the process name which runs on the
152 remote host, or nil."
153 (if (null (file-remote-p temporary-file-directory
))
154 (symbol-name file-notify--library
)
155 (and (consp file-notify--test-remote-enabled-checked
)
156 (processp (cdr file-notify--test-remote-enabled-checked
))
157 (replace-regexp-in-string
158 "<[[:digit:]]+>\\'" ""
159 (process-name (cdr file-notify--test-remote-enabled-checked
))))))
161 (defmacro file-notify--deftest-remote
(test docstring
)
162 "Define ert `TEST-remote' for remote files."
164 `(ert-deftest ,(intern (concat (symbol-name test
) "-remote")) ()
166 :tags
'(:expensive-test
)
167 (let* ((temporary-file-directory
168 file-notify-test-remote-temporary-file-directory
)
169 (file-notify--test-read-event-timeout 0.1)
170 (ert-test (ert-get-test ',test
)))
171 (skip-unless (file-notify--test-remote-enabled))
172 (tramp-cleanup-connection
173 (tramp-dissect-file-name temporary-file-directory
) nil
'keep-password
)
174 (funcall (ert-test-body ert-test
)))))
176 (ert-deftest file-notify-test00-availability
()
177 "Test availability of `file-notify'."
178 (skip-unless (file-notify--test-local-enabled))
179 ;; Report the native library which has been used.
180 (message "Library: `%s'" (file-notify--test-library))
182 (setq file-notify--test-desc
183 (file-notify-add-watch temporary-file-directory
'(change) 'ignore
)))
186 (file-notify--test-cleanup))
188 (file-notify--deftest-remote file-notify-test00-availability
189 "Test availability of `file-notify' for remote files.")
191 (ert-deftest file-notify-test01-add-watch
()
192 "Check `file-notify-add-watch'."
193 (skip-unless (file-notify--test-local-enabled))
195 (setq file-notify--test-tmpfile
(file-notify--test-make-temp-name)
196 file-notify--test-tmpfile1
197 (format "%s/%s" file-notify--test-tmpfile
(md5 (current-time-string))))
199 ;; Check, that different valid parameters are accepted.
201 (setq file-notify--test-desc
202 (file-notify-add-watch temporary-file-directory
'(change) 'ignore
)))
203 (file-notify-rm-watch file-notify--test-desc
)
205 (setq file-notify--test-desc
206 (file-notify-add-watch
207 temporary-file-directory
'(attribute-change) 'ignore
)))
208 (file-notify-rm-watch file-notify--test-desc
)
210 (setq file-notify--test-desc
211 (file-notify-add-watch
212 temporary-file-directory
'(change attribute-change
) 'ignore
)))
213 (file-notify-rm-watch file-notify--test-desc
)
214 (write-region "any text" nil file-notify--test-tmpfile nil
'no-message
)
216 (setq file-notify--test-desc
217 (file-notify-add-watch
218 file-notify--test-tmpfile
'(change attribute-change
) 'ignore
)))
219 (file-notify-rm-watch file-notify--test-desc
)
220 (delete-file file-notify--test-tmpfile
)
222 ;; Check error handling.
223 (should-error (file-notify-add-watch 1 2 3 4)
224 :type
'wrong-number-of-arguments
)
227 (file-notify-add-watch 1 2 3))
228 '(wrong-type-argument 1)))
231 (file-notify-add-watch temporary-file-directory
2 3))
232 '(wrong-type-argument 2)))
235 (file-notify-add-watch temporary-file-directory
'(change) 3))
236 '(wrong-type-argument 3)))
237 ;; The upper directory of a file must exist.
240 (file-notify-add-watch
241 file-notify--test-tmpfile1
'(change attribute-change
) 'ignore
))
243 "Directory does not exist" ,file-notify--test-tmpfile
)))
246 (file-notify--test-cleanup))
248 (file-notify--deftest-remote file-notify-test01-add-watch
249 "Check `file-notify-add-watch' for remote files.")
251 (defun file-notify--test-event-test ()
252 "Ert test function to be called by `file-notify--test-event-handler'.
253 We cannot pass arguments, so we assume that `file-notify--test-event'
255 ;; Check the descriptor.
256 (should (equal (car file-notify--test-event
) file-notify--test-desc
))
257 ;; Check the file name.
260 (file-notify--event-watched-file file-notify--test-event
)
261 (file-notify--event-file-name file-notify--test-event
)))
262 ;; Check the second file name if exists.
263 (when (eq (nth 1 file-notify--test-event
) 'renamed
)
266 (file-notify--event-watched-file file-notify--test-event
)
267 (file-notify--event-file1-name file-notify--test-event
)))))
269 (defun file-notify--test-event-handler (event)
270 "Run a test over FILE-NOTIFY--TEST-EVENT.
271 For later analysis, append the test result to `file-notify--test-results'
272 and the event to `file-notify--test-events'."
273 (let* ((file-notify--test-event event
)
275 (ert-run-test (make-ert-test :body
'file-notify--test-event-test
))))
276 ;; Do not add lock files, this would confuse the checks.
277 (unless (string-match
279 (file-notify--event-file-name file-notify--test-event
))
280 ;;(message "file-notify--test-event-handler result: %s event: %S"
281 ;;(null (ert-test-failed-p result)) file-notify--test-event)
282 (setq file-notify--test-events
283 (append file-notify--test-events
`(,file-notify--test-event
))
284 file-notify--test-results
285 (append file-notify--test-results
`(,result
))))))
287 (defun file-notify--test-make-temp-name ()
288 "Create a temporary file name for test."
290 (make-temp-name "file-notify-test") temporary-file-directory
))
292 (defmacro file-notify--wait-for-events
(timeout until
)
293 "Wait for and return file notification events until form UNTIL is true.
294 TIMEOUT is the maximum time to wait for, in seconds."
295 `(with-timeout (,timeout
(ignore))
297 (read-event nil nil file-notify--test-read-event-timeout
))))
299 (defun file-notify--test-with-events-check (events)
300 "Check whether received events match one of the EVENTS alternatives."
302 (dolist (elt events result
)
305 (equal elt
(mapcar #'cadr file-notify--test-events
)))))))
307 (defun file-notify--test-with-events-explainer (events)
308 "Explain why `file-notify--test-with-events-check' fails."
309 (if (null (cdr events
))
310 (format "Received events `%s' do not match expected events `%s'"
311 (mapcar #'cadr file-notify--test-events
) (car events
))
313 "Received events `%s' do not match any sequence of expected events `%s'"
314 (mapcar #'cadr file-notify--test-events
) events
)))
316 (put 'file-notify--test-with-events-check
'ert-explainer
317 'file-notify--test-with-events-explainer
)
319 (defmacro file-notify--test-with-events
(events &rest body
)
320 "Run BODY collecting events and then compare with EVENTS.
321 EVENTS is either a simple list of events, or a list of lists of
322 events, which represent different possible results. Don't wait
323 longer than timeout seconds for the events to be delivered."
325 `(let* ((events (if (consp (car ,events
)) ,events
(list ,events
)))
326 (max-length (apply 'max
(mapcar 'length events
)))
328 ;; Flush pending events.
329 (file-notify--wait-for-events
330 (file-notify--test-timeout)
332 (setq file-notify--test-events nil
333 file-notify--test-results nil
)
335 (file-notify--wait-for-events
336 ;; More events need more time. Use some fudge factor.
337 (* (ceiling max-length
100) (file-notify--test-timeout))
338 (= max-length
(length file-notify--test-events
)))
339 ;; Check the result sequence just to make sure that all events
341 (dolist (result file-notify--test-results
)
342 (when (ert-test-failed-p result
)
344 (cadr (ert-test-result-with-condition-condition result
)))))
345 ;; One of the possible event sequences shall match.
346 (should (file-notify--test-with-events-check events
))))
348 (ert-deftest file-notify-test02-events
()
349 "Check file creation/change/removal notifications."
350 (skip-unless (file-notify--test-local-enabled))
354 ;; Check file creation, change and deletion. It doesn't work
355 ;; for cygwin and kqueue, because we don't use an implicit
356 ;; directory monitor (kqueue), or the timings are too bad (cygwin).
357 (unless (or (eq system-type
'cygwin
)
358 (string-equal (file-notify--test-library) "kqueue"))
359 (setq file-notify--test-tmpfile
(file-notify--test-make-temp-name))
361 (setq file-notify--test-desc
362 (file-notify-add-watch
363 file-notify--test-tmpfile
364 '(change) 'file-notify--test-event-handler
)))
365 (file-notify--test-with-events
367 ;; cygwin recognizes only `deleted' and `stopped' events.
368 ((eq system-type
'cygwin
)
370 (t '(created changed deleted stopped
)))
372 "another text" nil file-notify--test-tmpfile nil
'no-message
)
373 (read-event nil nil file-notify--test-read-event-timeout
)
374 (delete-file file-notify--test-tmpfile
))
375 (file-notify-rm-watch file-notify--test-desc
))
377 ;; Check file change and deletion.
378 (setq file-notify--test-tmpfile
(file-notify--test-make-temp-name))
379 (write-region "any text" nil file-notify--test-tmpfile nil
'no-message
)
381 (setq file-notify--test-desc
382 (file-notify-add-watch
383 file-notify--test-tmpfile
384 '(change) 'file-notify--test-event-handler
)))
385 (file-notify--test-with-events
387 ;; cygwin recognizes only `deleted' and `stopped' events.
388 ((eq system-type
'cygwin
)
390 ;; inotify and kqueue raise just one `changed' event.
391 ((or (string-equal "inotify" (file-notify--test-library))
392 (string-equal "kqueue" (file-notify--test-library)))
393 '(changed deleted stopped
))
394 ;; gfilenotify raises one or two `changed' events
395 ;; randomly, no chance to test. So we accept both cases.
396 ((string-equal "gfilenotify" (file-notify--test-library))
397 '((changed deleted stopped
)
398 (changed changed deleted stopped
)))
399 (t '(changed changed deleted stopped
)))
400 (read-event nil nil file-notify--test-read-event-timeout
)
402 "another text" nil file-notify--test-tmpfile nil
'no-message
)
403 (read-event nil nil file-notify--test-read-event-timeout
)
404 (delete-file file-notify--test-tmpfile
))
405 (file-notify-rm-watch file-notify--test-desc
)
407 ;; Check file creation, change and deletion when watching a
408 ;; directory. There must be a `stopped' event when deleting
410 (let ((temporary-file-directory
411 (make-temp-file "file-notify-test-parent" t
)))
413 (setq file-notify--test-tmpfile
(file-notify--test-make-temp-name)
414 file-notify--test-desc
415 (file-notify-add-watch
416 temporary-file-directory
417 '(change) 'file-notify--test-event-handler
)))
418 (file-notify--test-with-events
420 ;; w32notify does not raise `deleted' and `stopped'
421 ;; events for the watched directory.
422 ((string-equal (file-notify--test-library) "w32notify")
423 '(created changed deleted
))
424 ;; cygwin recognizes only `deleted' and `stopped' events.
425 ((eq system-type
'cygwin
)
427 ;; There are two `deleted' events, for the file and for
428 ;; the directory. Except for kqueue.
429 ((string-equal (file-notify--test-library) "kqueue")
430 '(created changed deleted stopped
))
431 (t '(created changed deleted deleted stopped
)))
432 (read-event nil nil file-notify--test-read-event-timeout
)
434 "any text" nil file-notify--test-tmpfile nil
'no-message
)
435 (read-event nil nil file-notify--test-read-event-timeout
)
436 (delete-directory temporary-file-directory
'recursive
))
437 (file-notify-rm-watch file-notify--test-desc
))
439 ;; Check copy of files inside a directory.
440 (let ((temporary-file-directory
441 (make-temp-file "file-notify-test-parent" t
)))
443 (setq file-notify--test-tmpfile
(file-notify--test-make-temp-name)
444 file-notify--test-tmpfile1
(file-notify--test-make-temp-name)
445 file-notify--test-desc
446 (file-notify-add-watch
447 temporary-file-directory
448 '(change) 'file-notify--test-event-handler
)))
449 (file-notify--test-with-events
451 ;; w32notify does not distinguish between `changed' and
452 ;; `attribute-changed'. It does not raise `deleted'
453 ;; and `stopped' events for the watched directory.
454 ((string-equal (file-notify--test-library) "w32notify")
455 '(created changed created changed
456 changed changed changed
458 ;; cygwin recognizes only `deleted' and `stopped' events.
459 ((eq system-type
'cygwin
)
461 ;; There are three `deleted' events, for two files and
462 ;; for the directory. Except for kqueue.
463 ((string-equal (file-notify--test-library) "kqueue")
464 '(created changed created changed deleted stopped
))
465 (t '(created changed created changed
466 deleted deleted deleted stopped
)))
467 (read-event nil nil file-notify--test-read-event-timeout
)
469 "any text" nil file-notify--test-tmpfile nil
'no-message
)
470 (read-event nil nil file-notify--test-read-event-timeout
)
471 (copy-file file-notify--test-tmpfile file-notify--test-tmpfile1
)
472 ;; The next two events shall not be visible.
473 (read-event nil nil file-notify--test-read-event-timeout
)
474 (set-file-modes file-notify--test-tmpfile
000)
475 (read-event nil nil file-notify--test-read-event-timeout
)
476 (set-file-times file-notify--test-tmpfile
'(0 0))
477 (read-event nil nil file-notify--test-read-event-timeout
)
478 (delete-directory temporary-file-directory
'recursive
))
479 (file-notify-rm-watch file-notify--test-desc
))
481 ;; Check rename of files inside a directory.
482 (let ((temporary-file-directory
483 (make-temp-file "file-notify-test-parent" t
)))
485 (setq file-notify--test-tmpfile
(file-notify--test-make-temp-name)
486 file-notify--test-tmpfile1
(file-notify--test-make-temp-name)
487 file-notify--test-desc
488 (file-notify-add-watch
489 temporary-file-directory
490 '(change) 'file-notify--test-event-handler
)))
491 (file-notify--test-with-events
493 ;; w32notify does not raise `deleted' and `stopped'
494 ;; events for the watched directory.
495 ((string-equal (file-notify--test-library) "w32notify")
496 '(created changed renamed deleted
))
497 ;; cygwin recognizes only `deleted' and `stopped' events.
498 ((eq system-type
'cygwin
)
500 ;; There are two `deleted' events, for the file and for
501 ;; the directory. Except for kqueue.
502 ((string-equal (file-notify--test-library) "kqueue")
503 '(created changed renamed deleted stopped
))
504 (t '(created changed renamed deleted deleted stopped
)))
505 (read-event nil nil file-notify--test-read-event-timeout
)
507 "any text" nil file-notify--test-tmpfile nil
'no-message
)
508 (read-event nil nil file-notify--test-read-event-timeout
)
509 (rename-file file-notify--test-tmpfile file-notify--test-tmpfile1
)
510 ;; After the rename, we won't get events anymore.
511 (read-event nil nil file-notify--test-read-event-timeout
)
512 (delete-directory temporary-file-directory
'recursive
))
513 (file-notify-rm-watch file-notify--test-desc
))
515 ;; Check attribute change. Does not work for cygwin.
516 (unless (eq system-type
'cygwin
)
517 (setq file-notify--test-tmpfile
(file-notify--test-make-temp-name))
519 "any text" nil file-notify--test-tmpfile nil
'no-message
)
521 (setq file-notify--test-desc
522 (file-notify-add-watch
523 file-notify--test-tmpfile
524 '(attribute-change) 'file-notify--test-event-handler
)))
525 (file-notify--test-with-events
527 ;; w32notify does not distinguish between `changed' and
528 ;; `attribute-changed'.
529 ((string-equal (file-notify--test-library) "w32notify")
530 '(changed changed changed changed
))
531 ;; For kqueue and in the remote case, `write-region'
532 ;; raises also an `attribute-changed' event.
533 ((or (string-equal (file-notify--test-library) "kqueue")
534 (file-remote-p temporary-file-directory
))
535 '(attribute-changed attribute-changed attribute-changed
))
536 (t '(attribute-changed attribute-changed
)))
537 (read-event nil nil file-notify--test-read-event-timeout
)
539 "any text" nil file-notify--test-tmpfile nil
'no-message
)
540 (read-event nil nil file-notify--test-read-event-timeout
)
541 (set-file-modes file-notify--test-tmpfile
000)
542 (read-event nil nil file-notify--test-read-event-timeout
)
543 (set-file-times file-notify--test-tmpfile
'(0 0))
544 (read-event nil nil file-notify--test-read-event-timeout
)
545 (delete-file file-notify--test-tmpfile
))
546 (file-notify-rm-watch file-notify--test-desc
)))
549 (file-notify--test-cleanup)))
551 (file-notify--deftest-remote file-notify-test02-events
552 "Check file creation/change/removal notifications for remote files.")
554 (require 'autorevert
)
555 (setq auto-revert-notify-exclude-dir-regexp
"nothing-to-be-excluded"
556 auto-revert-remote-files t
557 auto-revert-stop-on-user-input nil
)
559 (ert-deftest file-notify-test03-autorevert
()
560 "Check autorevert via file notification."
561 (skip-unless (file-notify--test-local-enabled))
562 ;; `auto-revert-buffers' runs every 5". And we must wait, until the
563 ;; file has been reverted.
564 (let ((timeout (if (file-remote-p temporary-file-directory
) 60 10))
568 (setq file-notify--test-tmpfile
(file-notify--test-make-temp-name))
571 "any text" nil file-notify--test-tmpfile nil
'no-message
)
572 (setq buf
(find-file-noselect file-notify--test-tmpfile
))
573 (with-current-buffer buf
574 (should (string-equal (buffer-string) "any text"))
575 ;; `buffer-stale--default-function' checks for
576 ;; `verify-visited-file-modtime'. We must ensure that it
581 ;; `auto-revert-buffers' runs every 5".
582 (with-timeout (timeout (ignore))
583 (while (null auto-revert-notify-watch-descriptor
)
586 ;; Check, that file notification has been used.
587 (should auto-revert-mode
)
588 (should auto-revert-use-notify
)
589 (should auto-revert-notify-watch-descriptor
)
591 ;; Modify file. We wait for a second, in order to have
592 ;; another timestamp.
593 (with-current-buffer (get-buffer-create "*Messages*")
594 (narrow-to-region (point-max) (point-max)))
597 "another text" nil file-notify--test-tmpfile nil
'no-message
)
599 ;; Check, that the buffer has been reverted.
600 (with-current-buffer (get-buffer-create "*Messages*")
601 (file-notify--wait-for-events
604 (format-message "Reverting buffer `%s'." (buffer-name buf
))
606 (should (string-match "another text" (buffer-string)))
608 ;; Stop file notification. Autorevert shall still work via polling.
609 (file-notify-rm-watch auto-revert-notify-watch-descriptor
)
610 (file-notify--wait-for-events
611 timeout
(null auto-revert-use-notify
))
612 (should-not auto-revert-use-notify
)
613 (should-not auto-revert-notify-watch-descriptor
)
615 ;; Modify file. We wait for two seconds, in order to
616 ;; have another timestamp. One second seems to be too
618 (with-current-buffer (get-buffer-create "*Messages*")
619 (narrow-to-region (point-max) (point-max)))
622 "foo bla" nil file-notify--test-tmpfile nil
'no-message
)
624 ;; Check, that the buffer has been reverted.
625 (with-current-buffer (get-buffer-create "*Messages*")
626 (file-notify--wait-for-events
629 (format-message "Reverting buffer `%s'." (buffer-name buf
))
631 (should (string-match "foo bla" (buffer-string)))))
634 (with-current-buffer "*Messages*" (widen))
635 (ignore-errors (kill-buffer buf
))
636 (file-notify--test-cleanup))))
638 (file-notify--deftest-remote file-notify-test03-autorevert
639 "Check autorevert via file notification for remote files.")
641 (ert-deftest file-notify-test04-file-validity
()
642 "Check `file-notify-valid-p' for files."
643 (skip-unless (file-notify--test-local-enabled))
647 (setq file-notify--test-tmpfile
(file-notify--test-make-temp-name))
648 (write-region "any text" nil file-notify--test-tmpfile nil
'no-message
)
650 (setq file-notify--test-desc
651 (file-notify-add-watch
652 file-notify--test-tmpfile
653 '(change) #'file-notify--test-event-handler
)))
654 (should (file-notify-valid-p file-notify--test-desc
))
655 ;; After calling `file-notify-rm-watch', the descriptor is not
657 (file-notify-rm-watch file-notify--test-desc
)
658 (should-not (file-notify-valid-p file-notify--test-desc
))
659 (delete-file file-notify--test-tmpfile
))
662 (file-notify--test-cleanup))
666 (setq file-notify--test-tmpfile
(file-notify--test-make-temp-name))
667 (write-region "any text" nil file-notify--test-tmpfile nil
'no-message
)
669 (setq file-notify--test-desc
670 (file-notify-add-watch
671 file-notify--test-tmpfile
672 '(change) #'file-notify--test-event-handler
)))
673 (file-notify--test-with-events
675 ;; cygwin recognizes only `deleted' and `stopped' events.
676 ((eq system-type
'cygwin
)
678 ;; inotify and kqueue raise just one `changed' event.
679 ((or (string-equal "inotify" (file-notify--test-library))
680 (string-equal "kqueue" (file-notify--test-library)))
681 '(changed deleted stopped
))
682 ;; gfilenotify raises one or two `changed' events
683 ;; randomly, no chance to test. So we accept both cases.
684 ((string-equal "gfilenotify" (file-notify--test-library))
685 '((changed deleted stopped
)
686 (changed changed deleted stopped
)))
687 (t '(changed changed deleted stopped
)))
688 (should (file-notify-valid-p file-notify--test-desc
))
689 (read-event nil nil file-notify--test-read-event-timeout
)
691 "another text" nil file-notify--test-tmpfile nil
'no-message
)
692 (read-event nil nil file-notify--test-read-event-timeout
)
693 (delete-file file-notify--test-tmpfile
))
694 ;; After deleting the file, the descriptor is not valid anymore.
695 (should-not (file-notify-valid-p file-notify--test-desc
))
696 (file-notify-rm-watch file-notify--test-desc
))
699 (file-notify--test-cleanup))
702 (let ((temporary-file-directory
703 (make-temp-file "file-notify-test-parent" t
)))
705 (setq file-notify--test-tmpfile
(file-notify--test-make-temp-name)
706 file-notify--test-desc
707 (file-notify-add-watch
708 temporary-file-directory
709 '(change) #'file-notify--test-event-handler
)))
710 (file-notify--test-with-events
712 ;; w32notify does not raise `deleted' and `stopped' events
713 ;; for the watched directory.
714 ((string-equal (file-notify--test-library) "w32notify")
715 '(created changed deleted
))
716 ;; cygwin recognizes only `deleted' and `stopped' events.
717 ((eq system-type
'cygwin
)
719 ;; There are two `deleted' events, for the file and for the
720 ;; directory. Except for kqueue.
721 ((string-equal (file-notify--test-library) "kqueue")
722 '(created changed deleted stopped
))
723 (t '(created changed deleted deleted stopped
)))
724 (should (file-notify-valid-p file-notify--test-desc
))
725 (read-event nil nil file-notify--test-read-event-timeout
)
727 "any text" nil file-notify--test-tmpfile nil
'no-message
)
728 (read-event nil nil file-notify--test-read-event-timeout
)
729 (delete-directory temporary-file-directory t
))
730 ;; After deleting the parent directory, the descriptor must
731 ;; not be valid anymore.
732 (should-not (file-notify-valid-p file-notify--test-desc
)))
735 (file-notify--test-cleanup)))
737 (file-notify--deftest-remote file-notify-test04-file-validity
738 "Check `file-notify-valid-p' via file notification for remote files.")
740 (ert-deftest file-notify-test05-dir-validity
()
741 "Check `file-notify-valid-p' for directories."
742 (skip-unless (file-notify--test-local-enabled))
746 (setq file-notify--test-tmpfile
747 (file-name-as-directory (file-notify--test-make-temp-name)))
748 (make-directory file-notify--test-tmpfile
)
750 (setq file-notify--test-desc
751 (file-notify-add-watch
752 file-notify--test-tmpfile
753 '(change) #'file-notify--test-event-handler
)))
754 (should (file-notify-valid-p file-notify--test-desc
))
755 ;; After removing the watch, the descriptor must not be valid
757 (file-notify-rm-watch file-notify--test-desc
)
758 (file-notify--wait-for-events
759 (file-notify--test-timeout)
760 (not (file-notify-valid-p file-notify--test-desc
)))
761 (should-not (file-notify-valid-p file-notify--test-desc
)))
764 (file-notify--test-cleanup))
768 (setq file-notify--test-tmpfile
769 (file-name-as-directory (file-notify--test-make-temp-name)))
770 (make-directory file-notify--test-tmpfile
)
772 (setq file-notify--test-desc
773 (file-notify-add-watch
774 file-notify--test-tmpfile
775 '(change) #'file-notify--test-event-handler
)))
776 (should (file-notify-valid-p file-notify--test-desc
))
777 ;; After deleting the directory, the descriptor must not be
779 (delete-directory file-notify--test-tmpfile t
)
780 (file-notify--wait-for-events
781 (file-notify--test-timeout)
782 (not (file-notify-valid-p file-notify--test-desc
)))
783 (should-not (file-notify-valid-p file-notify--test-desc
)))
786 (file-notify--test-cleanup)))
788 (file-notify--deftest-remote file-notify-test05-dir-validity
789 "Check `file-notify-valid-p' via file notification for remote directories.")
791 (ert-deftest file-notify-test06-many-events
()
792 "Check that events are not dropped."
793 :tags
'(:expensive-test
)
794 (skip-unless (file-notify--test-local-enabled))
795 ;; Under cygwin events arrive in random order. Impossible to define a test.
796 (skip-unless (not (eq system-type
'cygwin
)))
798 (setq file-notify--test-tmpfile
(file-notify--test-make-temp-name))
799 (make-directory file-notify--test-tmpfile
)
801 (setq file-notify--test-desc
802 (file-notify-add-watch
803 file-notify--test-tmpfile
804 '(change) 'file-notify--test-event-handler
)))
807 source-file-list target-file-list
808 (default-directory file-notify--test-tmpfile
))
810 ;; It matters which direction we rename, at least for
811 ;; kqueue. This backend parses directories in alphabetic
812 ;; order (x%d before y%d). So we rename into both directions.
813 (if (zerop (mod i
2))
815 (push (expand-file-name (format "x%d" i
)) source-file-list
)
816 (push (expand-file-name (format "y%d" i
)) target-file-list
))
817 (push (expand-file-name (format "y%d" i
)) source-file-list
)
818 (push (expand-file-name (format "x%d" i
)) target-file-list
)))
819 (file-notify--test-with-events (make-list (+ n n
) 'created
)
820 (let ((source-file-list source-file-list
)
821 (target-file-list target-file-list
))
822 (while (and source-file-list target-file-list
)
823 (read-event nil nil file-notify--test-read-event-timeout
)
824 (write-region "" nil
(pop source-file-list
) nil
'no-message
)
825 (read-event nil nil file-notify--test-read-event-timeout
)
826 (write-region "" nil
(pop target-file-list
) nil
'no-message
))))
827 (file-notify--test-with-events
829 ;; w32notify fires both `deleted' and `renamed' events.
830 ((string-equal (file-notify--test-library) "w32notify")
833 (setq r
(append '(deleted renamed
) r
)))))
834 (t (make-list n
'renamed
)))
835 (let ((source-file-list source-file-list
)
836 (target-file-list target-file-list
))
837 (while (and source-file-list target-file-list
)
838 (read-event nil nil file-notify--test-read-event-timeout
)
839 (rename-file (pop source-file-list
) (pop target-file-list
) t
))))
840 (file-notify--test-with-events (make-list n
'deleted
)
841 (dolist (file target-file-list
)
842 (read-event nil nil file-notify--test-read-event-timeout
)
843 (delete-file file
) file-notify--test-read-event-timeout
)))
846 (file-notify--test-cleanup)))
848 (file-notify--deftest-remote file-notify-test06-many-events
849 "Check that events are not dropped for remote directories.")
851 (ert-deftest file-notify-test07-backup
()
852 "Check that backup keeps file notification."
853 (skip-unless (file-notify--test-local-enabled))
857 (setq file-notify--test-tmpfile
(file-notify--test-make-temp-name))
858 (write-region "any text" nil file-notify--test-tmpfile nil
'no-message
)
860 (setq file-notify--test-desc
861 (file-notify-add-watch
862 file-notify--test-tmpfile
863 '(change) #'file-notify--test-event-handler
)))
864 (should (file-notify-valid-p file-notify--test-desc
))
865 (file-notify--test-with-events
867 ;; For w32notify and in the remote case, there are two
869 ((or (string-equal (file-notify--test-library) "w32notify")
870 (file-remote-p temporary-file-directory
))
872 ;; gfilenotify raises one or two `changed' events
873 ;; randomly, no chance to test. So we accept both cases.
874 ((string-equal "gfilenotify" (file-notify--test-library))
878 ;; There shouldn't be any problem, because the file is kept.
880 (let ((buffer-file-name file-notify--test-tmpfile
)
881 (make-backup-files t
)
882 (backup-by-copying t
)
883 (kept-new-versions 1)
884 (delete-old-versions t
))
885 (insert "another text")
887 ;; After saving the buffer, the descriptor is still valid.
888 (should (file-notify-valid-p file-notify--test-desc
))
889 (delete-file file-notify--test-tmpfile
))
892 (file-notify--test-cleanup))
896 ;; It doesn't work for kqueue, because we don't use an
897 ;; implicit directory monitor.
898 (unless (string-equal (file-notify--test-library) "kqueue")
899 (setq file-notify--test-tmpfile
(file-notify--test-make-temp-name))
901 "any text" nil file-notify--test-tmpfile nil
'no-message
)
903 (setq file-notify--test-desc
904 (file-notify-add-watch
905 file-notify--test-tmpfile
906 '(change) #'file-notify--test-event-handler
)))
907 (should (file-notify-valid-p file-notify--test-desc
))
908 (file-notify--test-with-events '(renamed created changed
)
909 ;; The file is renamed when creating a backup. It shall
912 (let ((buffer-file-name file-notify--test-tmpfile
)
913 (make-backup-files t
)
914 (backup-by-copying nil
)
915 (backup-by-copying-when-mismatch nil
)
916 (kept-new-versions 1)
917 (delete-old-versions t
))
918 (insert "another text")
920 ;; After saving the buffer, the descriptor is still valid.
921 (should (file-notify-valid-p file-notify--test-desc
))
922 (delete-file file-notify--test-tmpfile
)))
925 (file-notify--test-cleanup)))
927 (file-notify--deftest-remote file-notify-test07-backup
928 "Check that backup keeps file notification for remote files.")
930 (ert-deftest file-notify-test08-watched-file-in-watched-dir
()
931 "Watches a directory and a file in that directory separately.
932 Checks that the callbacks are only called with events with
933 descriptors that were issued when registering the watches. This
934 test caters for the situation in bug#22736 where the callback for
935 the directory received events for the file with the descriptor of
937 :tags
'(:expensive-test
)
938 (skip-unless (file-notify--test-local-enabled))
940 ;; A directory to be watched.
942 (setq file-notify--test-tmpfile
943 (make-temp-file "file-notify-test-parent" t
)))
944 ;; A file to be watched.
946 (setq file-notify--test-tmpfile1
947 (let ((temporary-file-directory file-notify--test-tmpfile
))
948 (file-notify--test-make-temp-name))))
949 (write-region "any text" nil file-notify--test-tmpfile1 nil
'no-message
)
951 (cl-flet (;; Directory monitor.
952 (dir-callback (event)
953 (let ((file-notify--test-desc file-notify--test-desc1
))
954 (file-notify--test-event-handler event
)))
956 (file-callback (event)
957 (let ((file-notify--test-desc file-notify--test-desc2
))
958 (file-notify--test-event-handler event
))))
960 (setq file-notify--test-desc1
961 (file-notify-add-watch
962 file-notify--test-tmpfile
963 '(change) #'dir-callback
)))
965 (setq file-notify--test-desc2
966 (file-notify-add-watch
967 file-notify--test-tmpfile1
968 '(change) #'file-callback
)))
969 (should (file-notify-valid-p file-notify--test-desc1
))
970 (should (file-notify-valid-p file-notify--test-desc2
))
971 (should-not (equal file-notify--test-desc1 file-notify--test-desc2
))
972 ;; gfilenotify raises one or two `changed' events randomly in
973 ;; the file monitor, no chance to test.
974 (unless (string-equal "gfilenotify" (file-notify--test-library))
975 (let ((n 100) events
)
976 ;; Compute the expected events.
977 (dotimes (_i (/ n
2))
981 ;; Directory monitor and file monitor.
983 ;; In the remote case, there are two `changed'
985 ((file-remote-p temporary-file-directory
)
986 '(changed changed changed changed
))
987 ;; The directory monitor in kqueue does not
988 ;; raise any `changed' event. Just the file
989 ;; monitor event is received.
990 ((string-equal (file-notify--test-library) "kqueue")
992 ;; Otherwise, both monitors report the
994 (t '(changed changed
)))
995 ;; Just the directory monitor.
997 ;; In kqueue, there is an additional `changed'
999 ((string-equal (file-notify--test-library) "kqueue")
1000 '(changed created changed
))
1001 (t '(created changed
))))
1005 (file-notify--test-with-events events
1007 (read-event nil nil file-notify--test-read-event-timeout
)
1008 (if (zerop (mod i
2))
1010 "any text" nil file-notify--test-tmpfile1 t
'no-message
)
1011 (let ((temporary-file-directory file-notify--test-tmpfile
))
1014 (file-notify--test-make-temp-name) nil
'no-message
)))))))
1016 ;; If we delete the file, the directory monitor shall still be
1017 ;; active. We receive the `deleted' event from both the
1018 ;; directory and the file monitor. The `stopped' event is
1019 ;; from the file monitor. It's undecided in which order the
1020 ;; the directory and the file monitor are triggered.
1021 (file-notify--test-with-events
1022 '((deleted deleted stopped
)
1023 (deleted stopped deleted
))
1024 (delete-file file-notify--test-tmpfile1
))
1025 (should (file-notify-valid-p file-notify--test-desc1
))
1026 (should-not (file-notify-valid-p file-notify--test-desc2
))
1028 ;; Now we delete the directory.
1029 (file-notify--test-with-events
1031 ;; In kqueue, just one `deleted' event for the directory
1033 ((string-equal (file-notify--test-library) "kqueue")
1036 ;; The directory monitor raises a `deleted' event for
1037 ;; every file contained in the directory, we must
1042 file-notify--test-tmpfile nil
1043 directory-files-no-dot-files-regexp
'nosort
))
1045 ;; The events of the directory itself.
1047 ;; w32notify does not raise `deleted' and `stopped'
1048 ;; events for the watched directory.
1049 ((string-equal (file-notify--test-library) "w32notify") '())
1050 (t '(deleted stopped
))))))
1051 (delete-directory file-notify--test-tmpfile
'recursive
))
1052 (should-not (file-notify-valid-p file-notify--test-desc1
))
1053 (should-not (file-notify-valid-p file-notify--test-desc2
)))
1056 (file-notify--test-cleanup)))
1058 (file-notify--deftest-remote file-notify-test08-watched-file-in-watched-dir
1059 "Check `file-notify-test08-watched-file-in-watched-dir' for remote files.")
1061 (defun file-notify-test-all (&optional interactive
)
1062 "Run all tests for \\[file-notify]."
1065 (ert-run-tests-interactively "^file-notify-")
1066 (ert-run-tests-batch "^file-notify-")))
1070 ;; * kqueue does not send all expected `deleted' events. Maybe due to
1071 ;; the missing directory monitor.
1072 ;; * For w32notify, no `deleted' and `stopped' events arrive when a
1073 ;; directory is removed.
1074 ;; * For w32notify, no `attribute-changed' events arrive. Its sends
1075 ;; `changed' events instead.
1076 ;; * Check, why cygwin recognizes only `deleted' and `stopped' events.
1078 (provide 'file-notify-tests
)
1079 ;;; file-notify-tests.el ends here