Merge from origin/emacs-25
[emacs.git] / test / lisp / filenotify-tests.el
blob2c085b34de940e69b1f2967048df81b459ae41d5
1 ;;; filenotify-tests.el --- Tests of file notifications -*- lexical-binding: t; -*-
3 ;; Copyright (C) 2013-2017 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-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 (defun file-notify--test-read-event ()
68 "Read one event.
69 There are different timeouts for local and remote file notification libraries."
70 (read-event
71 nil nil
72 (cond
73 ;; gio/gpollfilemonitor.c declares POLL_TIME_SECS 5. So we must
74 ;; wait at least this time in the GPollFileMonitor case. A
75 ;; similar timeout seems to be needed in the GFamFileMonitor case,
76 ;; at least on Cygwin.
77 ((and (string-equal (file-notify--test-library) "gfilenotify")
78 (memq (file-notify--test-monitor)
79 '(GFamFileMonitor GPollFileMonitor)))
81 ((file-remote-p temporary-file-directory) 0.1)
82 (t 0.01))))
84 (defun file-notify--test-timeout ()
85 "Timeout to wait for arriving a bunch of events, in seconds."
86 (cond
87 ((file-remote-p temporary-file-directory) 6)
88 ((string-equal (file-notify--test-library) "w32notify") 4)
89 ((eq system-type 'cygwin) 6)
90 (t 3)))
92 (defmacro file-notify--wait-for-events (timeout until)
93 "Wait for and return file notification events until form UNTIL is true.
94 TIMEOUT is the maximum time to wait for, in seconds."
95 `(with-timeout (,timeout (ignore))
96 (while (null ,until)
97 (file-notify--test-read-event))))
99 (defun file-notify--test-no-descriptors ()
100 "Check that `file-notify-descriptors' is an empty hash table.
101 Return nil when any other file notification watch is still active."
102 ;; Give read events a last chance.
103 (file-notify--wait-for-events
104 (file-notify--test-timeout)
105 (zerop (hash-table-count file-notify-descriptors)))
106 ;; Now check.
107 (zerop (hash-table-count file-notify-descriptors)))
109 (defun file-notify--test-no-descriptors-explainer ()
110 "Explain why `file-notify--test-no-descriptors' fails."
111 (let ((result (list "Watch descriptor(s) existent:")))
112 (maphash
113 (lambda (key value) (push (cons key value) result))
114 file-notify-descriptors)
115 (nreverse result)))
117 (put 'file-notify--test-no-descriptors 'ert-explainer
118 'file-notify--test-no-descriptors-explainer)
120 (defun file-notify--test-cleanup-p ()
121 "Check, that the test has cleaned up the environment as much as needed."
122 ;; `file-notify--test-event' should not be set but bound
123 ;; dynamically.
124 (should-not file-notify--test-event)
125 ;; The test should have cleaned up this already. Let's check
126 ;; nevertheless.
127 (should (file-notify--test-no-descriptors)))
129 (defun file-notify--test-cleanup ()
130 "Cleanup after a test."
131 (file-notify-rm-watch file-notify--test-desc)
132 (file-notify-rm-watch file-notify--test-desc1)
133 (file-notify-rm-watch file-notify--test-desc2)
135 (ignore-errors
136 (delete-file (file-newest-backup file-notify--test-tmpfile)))
137 (ignore-errors
138 (if (file-directory-p file-notify--test-tmpfile)
139 (delete-directory file-notify--test-tmpfile 'recursive)
140 (delete-file file-notify--test-tmpfile)))
141 (ignore-errors
142 (if (file-directory-p file-notify--test-tmpfile1)
143 (delete-directory file-notify--test-tmpfile1 'recursive)
144 (delete-file file-notify--test-tmpfile1)))
145 (ignore-errors
146 (when (file-remote-p temporary-file-directory)
147 (tramp-cleanup-connection
148 (tramp-dissect-file-name temporary-file-directory) nil 'keep-password)))
150 (setq file-notify--test-tmpfile nil
151 file-notify--test-tmpfile1 nil
152 file-notify--test-desc nil
153 file-notify--test-desc1 nil
154 file-notify--test-desc2 nil
155 file-notify--test-results nil
156 file-notify--test-events nil))
158 (setq password-cache-expiry nil
159 tramp-verbose 0
160 tramp-message-show-message nil)
162 ;; This shall happen on hydra only.
163 (when (getenv "NIX_STORE")
164 (add-to-list 'tramp-remote-path 'tramp-own-remote-path))
166 ;; We do not want to try and fail `file-notify-add-watch'.
167 (defun file-notify--test-local-enabled ()
168 "Whether local file notification is enabled.
169 This is needed for local `temporary-file-directory' only, in the
170 remote case we return always t."
171 (or file-notify--library
172 (file-remote-p temporary-file-directory)))
174 (defvar file-notify--test-remote-enabled-checked nil
175 "Cached result of `file-notify--test-remote-enabled'.
176 If the function did run, the value is a cons cell, the `cdr'
177 being the result.")
179 (defun file-notify--test-remote-enabled ()
180 "Whether remote file notification is enabled."
181 (unless (consp file-notify--test-remote-enabled-checked)
182 (let (desc)
183 (ignore-errors
184 (and
185 (file-remote-p file-notify-test-remote-temporary-file-directory)
186 (file-directory-p file-notify-test-remote-temporary-file-directory)
187 (file-writable-p file-notify-test-remote-temporary-file-directory)
188 (setq desc
189 (file-notify-add-watch
190 file-notify-test-remote-temporary-file-directory
191 '(change) #'ignore))))
192 (setq file-notify--test-remote-enabled-checked (cons t desc))
193 (when desc (file-notify-rm-watch desc))))
194 ;; Return result.
195 (cdr file-notify--test-remote-enabled-checked))
197 (defun file-notify--test-library ()
198 "The used library for the test, as a string.
199 In the remote case, it is the process name which runs on the
200 remote host, or nil."
201 (if (null (file-remote-p temporary-file-directory))
202 (symbol-name file-notify--library)
203 (and (consp file-notify--test-remote-enabled-checked)
204 (processp (cdr file-notify--test-remote-enabled-checked))
205 (replace-regexp-in-string
206 "<[[:digit:]]+>\\'" ""
207 (process-name (cdr file-notify--test-remote-enabled-checked))))))
209 (defun file-notify--test-monitor ()
210 "The used monitor for the test, as a symbol.
211 This returns only for the local case and gfilenotify; otherwise it is nil.
212 `file-notify--test-desc' must be a valid watch descriptor."
213 (and file-notify--test-desc
214 (null (file-remote-p temporary-file-directory))
215 (functionp 'gfile-monitor-name)
216 (gfile-monitor-name file-notify--test-desc)))
218 (defmacro file-notify--deftest-remote (test docstring)
219 "Define ert `TEST-remote' for remote files."
220 (declare (indent 1))
221 `(ert-deftest ,(intern (concat (symbol-name test) "-remote")) ()
222 ,docstring
223 :tags '(:expensive-test)
224 (let* ((temporary-file-directory
225 file-notify-test-remote-temporary-file-directory)
226 (ert-test (ert-get-test ',test)))
227 (skip-unless (file-notify--test-remote-enabled))
228 (tramp-cleanup-connection
229 (tramp-dissect-file-name temporary-file-directory) nil 'keep-password)
230 (funcall (ert-test-body ert-test)))))
232 (ert-deftest file-notify-test00-availability ()
233 "Test availability of `file-notify'."
234 (skip-unless (file-notify--test-local-enabled))
236 (unwind-protect
237 (progn
238 ;; Report the native library which has been used.
239 (message "Library: `%s'" (file-notify--test-library))
240 (should
241 (setq file-notify--test-desc
242 (file-notify-add-watch
243 temporary-file-directory '(change) #'ignore)))
244 (when (file-notify--test-monitor)
245 (message "Monitor: `%s'" (file-notify--test-monitor)))
246 (file-notify-rm-watch file-notify--test-desc)
248 ;; The environment shall be cleaned up.
249 (file-notify--test-cleanup-p))
251 ;; Cleanup.
252 (file-notify--test-cleanup)))
254 (file-notify--deftest-remote file-notify-test00-availability
255 "Test availability of `file-notify' for remote files.")
257 (ert-deftest file-notify-test01-add-watch ()
258 "Check `file-notify-add-watch'."
259 (skip-unless (file-notify--test-local-enabled))
261 (unwind-protect
262 (progn
263 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
264 file-notify--test-tmpfile1
265 (format
266 "%s/%s" file-notify--test-tmpfile (md5 (current-time-string))))
268 ;; Check, that different valid parameters are accepted.
269 (should
270 (setq file-notify--test-desc
271 (file-notify-add-watch
272 temporary-file-directory '(change) #'ignore)))
273 (file-notify-rm-watch file-notify--test-desc)
274 (should
275 (setq file-notify--test-desc
276 (file-notify-add-watch
277 temporary-file-directory '(attribute-change) #'ignore)))
278 (file-notify-rm-watch file-notify--test-desc)
279 (should
280 (setq file-notify--test-desc
281 (file-notify-add-watch
282 temporary-file-directory '(change attribute-change) #'ignore)))
283 (file-notify-rm-watch file-notify--test-desc)
284 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
285 (should
286 (setq file-notify--test-desc
287 (file-notify-add-watch
288 file-notify--test-tmpfile '(change attribute-change) #'ignore)))
289 (file-notify-rm-watch file-notify--test-desc)
290 (delete-file file-notify--test-tmpfile)
292 ;; Check error handling.
293 (should-error (file-notify-add-watch 1 2 3 4)
294 :type 'wrong-number-of-arguments)
295 (should
296 (equal (should-error
297 (file-notify-add-watch 1 2 3))
298 '(wrong-type-argument 1)))
299 (should
300 (equal (should-error
301 (file-notify-add-watch temporary-file-directory 2 3))
302 '(wrong-type-argument 2)))
303 (should
304 (equal (should-error
305 (file-notify-add-watch temporary-file-directory '(change) 3))
306 '(wrong-type-argument 3)))
307 ;; The upper directory of a file must exist.
308 (should
309 (equal (should-error
310 (file-notify-add-watch
311 file-notify--test-tmpfile1
312 '(change attribute-change) #'ignore))
313 `(file-notify-error
314 "Directory does not exist" ,file-notify--test-tmpfile)))
316 ;; The environment shall be cleaned up.
317 (file-notify--test-cleanup-p))
319 ;; Cleanup.
320 (file-notify--test-cleanup)))
322 (file-notify--deftest-remote file-notify-test01-add-watch
323 "Check `file-notify-add-watch' for remote files.")
325 (defun file-notify--test-event-test ()
326 "Ert test function to be called by `file-notify--test-event-handler'.
327 We cannot pass arguments, so we assume that `file-notify--test-event'
328 is bound somewhere."
329 ;; Check the descriptor.
330 (should (equal (car file-notify--test-event) file-notify--test-desc))
331 ;; Check the file name.
332 (should
333 (string-prefix-p
334 (file-notify--event-watched-file file-notify--test-event)
335 (file-notify--event-file-name file-notify--test-event)))
336 ;; Check the second file name if exists.
337 (when (eq (nth 1 file-notify--test-event) 'renamed)
338 (should
339 (string-prefix-p
340 (file-notify--event-watched-file file-notify--test-event)
341 (file-notify--event-file1-name file-notify--test-event)))))
343 (defun file-notify--test-event-handler (event)
344 "Run a test over FILE-NOTIFY--TEST-EVENT.
345 For later analysis, append the test result to `file-notify--test-results'
346 and the event to `file-notify--test-events'."
347 (let* ((file-notify--test-event event)
348 (result
349 (ert-run-test (make-ert-test :body 'file-notify--test-event-test))))
350 ;; Do not add lock files, this would confuse the checks.
351 (unless (string-match
352 (regexp-quote ".#")
353 (file-notify--event-file-name file-notify--test-event))
354 ;;(message "file-notify--test-event-handler result: %s event: %S"
355 ;;(null (ert-test-failed-p result)) file-notify--test-event)
356 (setq file-notify--test-events
357 (append file-notify--test-events `(,file-notify--test-event))
358 file-notify--test-results
359 (append file-notify--test-results `(,result))))))
361 (defun file-notify--test-make-temp-name ()
362 "Create a temporary file name for test."
363 (expand-file-name
364 (make-temp-name "file-notify-test") temporary-file-directory))
366 (defun file-notify--test-with-events-check (events)
367 "Check whether received events match one of the EVENTS alternatives."
368 (let (result)
369 (dolist (elt events result)
370 (setq result
371 (or result
372 (if (eq (car elt) :random)
373 (equal (sort (cdr elt) 'string-lessp)
374 (sort (mapcar #'cadr file-notify--test-events)
375 'string-lessp))
376 (equal elt (mapcar #'cadr file-notify--test-events))))))))
378 (defun file-notify--test-with-events-explainer (events)
379 "Explain why `file-notify--test-with-events-check' fails."
380 (if (null (cdr events))
381 (format "Received events do not match expected events\n%s\n%s"
382 (mapcar #'cadr file-notify--test-events) (car events))
383 (format
384 "Received events do not match any sequence of expected events\n%s\n%s"
385 (mapcar #'cadr file-notify--test-events) events)))
387 (put 'file-notify--test-with-events-check 'ert-explainer
388 'file-notify--test-with-events-explainer)
390 (defmacro file-notify--test-with-events (events &rest body)
391 "Run BODY collecting events and then compare with EVENTS.
392 EVENTS is either a simple list of events, or a list of lists of
393 events, which represent different possible results. The first
394 event of a list could be the pseudo event `:random', which is
395 just an indicator for comparison.
397 Don't wait longer than timeout seconds for the events to be
398 delivered."
399 (declare (indent 1))
400 `(let* ((events (if (consp (car ,events)) ,events (list ,events)))
401 (max-length
402 (apply
403 'max
404 (mapcar
405 (lambda (x) (length (if (eq (car x) :random) (cdr x) x)))
406 events)))
407 create-lockfiles)
408 ;; Flush pending events.
409 (file-notify--test-read-event)
410 (file-notify--wait-for-events
411 (file-notify--test-timeout)
412 (not (input-pending-p)))
413 (setq file-notify--test-events nil
414 file-notify--test-results nil)
415 ,@body
416 (file-notify--wait-for-events
417 ;; More events need more time. Use some fudge factor.
418 (* (ceiling max-length 100) (file-notify--test-timeout))
419 (= max-length (length file-notify--test-events)))
420 ;; Check the result sequence just to make sure that all events
421 ;; are as expected.
422 (dolist (result file-notify--test-results)
423 (when (ert-test-failed-p result)
424 (ert-fail
425 (cadr (ert-test-result-with-condition-condition result)))))
426 ;; One of the possible event sequences shall match.
427 (should (file-notify--test-with-events-check events))))
429 (ert-deftest file-notify-test02-events ()
430 "Check file creation/change/removal notifications."
431 (skip-unless (file-notify--test-local-enabled))
433 (unwind-protect
434 (progn
435 ;; Check file creation, change and deletion. It doesn't work
436 ;; for kqueue, because we don't use an implicit directory
437 ;; monitor.
438 (unless (string-equal (file-notify--test-library) "kqueue")
439 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
440 (should
441 (setq file-notify--test-desc
442 (file-notify-add-watch
443 file-notify--test-tmpfile
444 '(change) #'file-notify--test-event-handler)))
445 (file-notify--test-with-events
446 (cond
447 ;; cygwin does not raise a `changed' event.
448 ((eq system-type 'cygwin)
449 '(created deleted stopped))
450 (t '(created changed deleted stopped)))
451 (write-region
452 "another text" nil file-notify--test-tmpfile nil 'no-message)
453 (file-notify--test-read-event)
454 (delete-file file-notify--test-tmpfile))
455 (file-notify-rm-watch file-notify--test-desc))
457 ;; Check file change and deletion.
458 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
459 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
460 (should
461 (setq file-notify--test-desc
462 (file-notify-add-watch
463 file-notify--test-tmpfile
464 '(change) #'file-notify--test-event-handler)))
465 (file-notify--test-with-events
466 ;; There could be one or two `changed' events.
467 '((changed deleted stopped)
468 (changed changed deleted stopped))
469 (write-region
470 "another text" nil file-notify--test-tmpfile nil 'no-message)
471 (file-notify--test-read-event)
472 (delete-file file-notify--test-tmpfile))
473 (file-notify-rm-watch file-notify--test-desc)
475 ;; Check file creation, change and deletion when watching a
476 ;; directory. There must be a `stopped' event when deleting
477 ;; the directory.
478 (let ((temporary-file-directory
479 (make-temp-file "file-notify-test-parent" t)))
480 (should
481 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
482 file-notify--test-desc
483 (file-notify-add-watch
484 temporary-file-directory
485 '(change) #'file-notify--test-event-handler)))
486 (file-notify--test-with-events
487 (cond
488 ;; w32notify does not raise `deleted' and `stopped'
489 ;; events for the watched directory.
490 ((string-equal (file-notify--test-library) "w32notify")
491 '(created changed deleted))
492 ;; There are two `deleted' events, for the file and for
493 ;; the directory. Except for cygwin and kqueue. And
494 ;; cygwin does not raise a `changed' event.
495 ((eq system-type 'cygwin)
496 '(created deleted stopped))
497 ((string-equal (file-notify--test-library) "kqueue")
498 '(created changed deleted stopped))
499 (t '(created changed deleted deleted stopped)))
500 (write-region
501 "any text" nil file-notify--test-tmpfile nil 'no-message)
502 (file-notify--test-read-event)
503 (delete-directory temporary-file-directory 'recursive))
504 (file-notify-rm-watch file-notify--test-desc))
506 ;; Check copy of files inside a directory.
507 (let ((temporary-file-directory
508 (make-temp-file "file-notify-test-parent" t)))
509 (should
510 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
511 file-notify--test-tmpfile1 (file-notify--test-make-temp-name)
512 file-notify--test-desc
513 (file-notify-add-watch
514 temporary-file-directory
515 '(change) #'file-notify--test-event-handler)))
516 (file-notify--test-with-events
517 (cond
518 ;; w32notify does not distinguish between `changed' and
519 ;; `attribute-changed'. It does not raise `deleted'
520 ;; and `stopped' events for the watched directory.
521 ((string-equal (file-notify--test-library) "w32notify")
522 '(created changed created changed
523 changed changed changed
524 deleted deleted))
525 ;; There are three `deleted' events, for two files and
526 ;; for the directory. Except for cygwin and kqueue.
527 ((eq system-type 'cygwin)
528 '(created created changed changed deleted stopped))
529 ((string-equal (file-notify--test-library) "kqueue")
530 '(created changed created changed deleted stopped))
531 (t '(created changed created changed
532 deleted deleted deleted stopped)))
533 (write-region
534 "any text" nil file-notify--test-tmpfile nil 'no-message)
535 (file-notify--test-read-event)
536 (copy-file file-notify--test-tmpfile file-notify--test-tmpfile1)
537 ;; The next two events shall not be visible.
538 (file-notify--test-read-event)
539 (set-file-modes file-notify--test-tmpfile 000)
540 (file-notify--test-read-event)
541 (set-file-times file-notify--test-tmpfile '(0 0))
542 (file-notify--test-read-event)
543 (delete-directory temporary-file-directory 'recursive))
544 (file-notify-rm-watch file-notify--test-desc))
546 ;; Check rename of files inside a directory.
547 (let ((temporary-file-directory
548 (make-temp-file "file-notify-test-parent" t)))
549 (should
550 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
551 file-notify--test-tmpfile1 (file-notify--test-make-temp-name)
552 file-notify--test-desc
553 (file-notify-add-watch
554 temporary-file-directory
555 '(change) #'file-notify--test-event-handler)))
556 (file-notify--test-with-events
557 (cond
558 ;; w32notify does not raise `deleted' and `stopped'
559 ;; events for the watched directory.
560 ((string-equal (file-notify--test-library) "w32notify")
561 '(created changed renamed deleted))
562 ;; There are two `deleted' events, for the file and for
563 ;; the directory. Except for cygwin and kqueue. And
564 ;; cygwin raises `created' and `deleted' events instead
565 ;; of a `renamed' event.
566 ((eq system-type 'cygwin)
567 '(created created deleted deleted stopped))
568 ((string-equal (file-notify--test-library) "kqueue")
569 '(created changed renamed deleted stopped))
570 (t '(created changed renamed deleted deleted stopped)))
571 (write-region
572 "any text" nil file-notify--test-tmpfile nil 'no-message)
573 (file-notify--test-read-event)
574 (rename-file file-notify--test-tmpfile file-notify--test-tmpfile1)
575 ;; After the rename, we won't get events anymore.
576 (file-notify--test-read-event)
577 (delete-directory temporary-file-directory 'recursive))
578 (file-notify-rm-watch file-notify--test-desc))
580 ;; Check attribute change. Does not work for cygwin.
581 (unless (and (eq system-type 'cygwin)
582 (not (file-remote-p temporary-file-directory)))
583 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
584 (write-region
585 "any text" nil file-notify--test-tmpfile nil 'no-message)
586 (should
587 (setq file-notify--test-desc
588 (file-notify-add-watch
589 file-notify--test-tmpfile
590 '(attribute-change) #'file-notify--test-event-handler)))
591 (file-notify--test-with-events
592 (cond
593 ;; w32notify does not distinguish between `changed' and
594 ;; `attribute-changed'. Under MS Windows 7, we get
595 ;; four `changed' events, and under MS Windows 10 just
596 ;; two. Strange.
597 ((string-equal (file-notify--test-library) "w32notify")
598 '((changed changed)
599 (changed changed changed changed)))
600 ;; For kqueue and in the remote case, `write-region'
601 ;; raises also an `attribute-changed' event.
602 ((or (string-equal (file-notify--test-library) "kqueue")
603 (file-remote-p temporary-file-directory))
604 '(attribute-changed attribute-changed attribute-changed))
605 (t '(attribute-changed attribute-changed)))
606 (write-region
607 "any text" nil file-notify--test-tmpfile nil 'no-message)
608 (file-notify--test-read-event)
609 (set-file-modes file-notify--test-tmpfile 000)
610 (file-notify--test-read-event)
611 (set-file-times file-notify--test-tmpfile '(0 0))
612 (file-notify--test-read-event)
613 (delete-file file-notify--test-tmpfile))
614 (file-notify-rm-watch file-notify--test-desc))
616 ;; The environment shall be cleaned up.
617 (file-notify--test-cleanup-p))
619 ;; Cleanup.
620 (file-notify--test-cleanup)))
622 (file-notify--deftest-remote file-notify-test02-events
623 "Check file creation/change/removal notifications for remote files.")
625 (require 'autorevert)
626 (setq auto-revert-notify-exclude-dir-regexp "nothing-to-be-excluded"
627 auto-revert-remote-files t
628 auto-revert-stop-on-user-input nil)
630 (ert-deftest file-notify-test03-autorevert ()
631 "Check autorevert via file notification."
632 (skip-unless (file-notify--test-local-enabled))
634 ;; `auto-revert-buffers' runs every 5". And we must wait, until the
635 ;; file has been reverted.
636 (let ((timeout (if (file-remote-p temporary-file-directory) 60 10))
637 buf)
638 (unwind-protect
639 (progn
640 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
641 (write-region
642 "any text" nil file-notify--test-tmpfile nil 'no-message)
643 (setq buf (find-file-noselect file-notify--test-tmpfile))
644 (with-current-buffer buf
645 (should (string-equal (buffer-string) "any text"))
646 ;; `buffer-stale--default-function' checks for
647 ;; `verify-visited-file-modtime'. We must ensure that it
648 ;; returns nil.
649 (sleep-for 1)
650 (auto-revert-mode 1)
652 ;; `auto-revert-buffers' runs every 5".
653 (with-timeout (timeout (ignore))
654 (while (null auto-revert-notify-watch-descriptor)
655 (sleep-for 1)))
657 ;; Check, that file notification has been used.
658 (should auto-revert-mode)
659 (should auto-revert-use-notify)
660 (should auto-revert-notify-watch-descriptor)
662 ;; Modify file. We wait for a second, in order to have
663 ;; another timestamp.
664 (with-current-buffer (get-buffer-create "*Messages*")
665 (narrow-to-region (point-max) (point-max)))
666 (sleep-for 1)
667 (write-region
668 "another text" nil file-notify--test-tmpfile nil 'no-message)
670 ;; Check, that the buffer has been reverted.
671 (with-current-buffer (get-buffer-create "*Messages*")
672 (file-notify--wait-for-events
673 timeout
674 (string-match
675 (format-message "Reverting buffer `%s'." (buffer-name buf))
676 (buffer-string))))
677 (should (string-match "another text" (buffer-string)))
679 ;; Stop file notification. Autorevert shall still work via polling.
680 (file-notify-rm-watch auto-revert-notify-watch-descriptor)
681 (file-notify--wait-for-events
682 timeout (null auto-revert-use-notify))
683 (should-not auto-revert-use-notify)
684 (should-not auto-revert-notify-watch-descriptor)
686 ;; Modify file. We wait for two seconds, in order to
687 ;; have another timestamp. One second seems to be too
688 ;; short.
689 (with-current-buffer (get-buffer-create "*Messages*")
690 (narrow-to-region (point-max) (point-max)))
691 (sleep-for 2)
692 (write-region
693 "foo bla" nil file-notify--test-tmpfile nil 'no-message)
695 ;; Check, that the buffer has been reverted.
696 (with-current-buffer (get-buffer-create "*Messages*")
697 (file-notify--wait-for-events
698 timeout
699 (string-match
700 (format-message "Reverting buffer `%s'." (buffer-name buf))
701 (buffer-string))))
702 (should (string-match "foo bla" (buffer-string))))
704 ;; The environment shall be cleaned up.
705 (file-notify--test-cleanup-p))
707 ;; Cleanup.
708 (with-current-buffer "*Messages*" (widen))
709 (ignore-errors (kill-buffer buf))
710 (file-notify--test-cleanup))))
712 (file-notify--deftest-remote file-notify-test03-autorevert
713 "Check autorevert via file notification for remote files.")
715 (ert-deftest file-notify-test04-file-validity ()
716 "Check `file-notify-valid-p' for files."
717 (skip-unless (file-notify--test-local-enabled))
719 (unwind-protect
720 (progn
721 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
722 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
723 (should
724 (setq file-notify--test-desc
725 (file-notify-add-watch
726 file-notify--test-tmpfile '(change) #'ignore)))
727 (should (file-notify-valid-p file-notify--test-desc))
728 ;; After calling `file-notify-rm-watch', the descriptor is not
729 ;; valid anymore.
730 (file-notify-rm-watch file-notify--test-desc)
731 (should-not (file-notify-valid-p file-notify--test-desc))
732 (delete-file file-notify--test-tmpfile)
734 ;; The environment shall be cleaned up.
735 (file-notify--test-cleanup-p))
737 ;; Cleanup.
738 (file-notify--test-cleanup))
740 (unwind-protect
741 (progn
742 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
743 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
744 (should
745 (setq file-notify--test-desc
746 (file-notify-add-watch
747 file-notify--test-tmpfile
748 '(change) #'file-notify--test-event-handler)))
749 (should (file-notify-valid-p file-notify--test-desc))
750 (file-notify--test-with-events
751 ;; There could be one or two `changed' events.
752 '((changed deleted stopped)
753 (changed changed deleted stopped))
754 (write-region
755 "another text" nil file-notify--test-tmpfile nil 'no-message)
756 (file-notify--test-read-event)
757 (delete-file file-notify--test-tmpfile))
758 ;; After deleting the file, the descriptor is not valid anymore.
759 (should-not (file-notify-valid-p file-notify--test-desc))
760 (file-notify-rm-watch file-notify--test-desc)
762 ;; The environment shall be cleaned up.
763 (file-notify--test-cleanup-p))
765 ;; Cleanup.
766 (file-notify--test-cleanup))
768 (unwind-protect
769 (let ((temporary-file-directory
770 (make-temp-file "file-notify-test-parent" t)))
771 (should
772 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
773 file-notify--test-desc
774 (file-notify-add-watch
775 temporary-file-directory
776 '(change) #'file-notify--test-event-handler)))
777 (should (file-notify-valid-p file-notify--test-desc))
778 (file-notify--test-with-events
779 (cond
780 ;; w32notify does not raise `deleted' and `stopped' events
781 ;; for the watched directory.
782 ((string-equal (file-notify--test-library) "w32notify")
783 '(created changed deleted))
784 ;; There are two `deleted' events, for the file and for the
785 ;; directory. Except for cygwin and kqueue. And cygwin
786 ;; does not raise a `changed' event.
787 ((eq system-type 'cygwin)
788 '(created deleted stopped))
789 ((string-equal (file-notify--test-library) "kqueue")
790 '(created changed deleted stopped))
791 (t '(created changed deleted deleted stopped)))
792 (write-region
793 "any text" nil file-notify--test-tmpfile nil 'no-message)
794 (file-notify--test-read-event)
795 (delete-directory temporary-file-directory t))
796 ;; After deleting the parent directory, the descriptor must
797 ;; not be valid anymore.
798 (should-not (file-notify-valid-p file-notify--test-desc))
800 ;; The environment shall be cleaned up.
801 (file-notify--test-cleanup-p))
803 ;; Cleanup.
804 (file-notify--test-cleanup)))
806 (file-notify--deftest-remote file-notify-test04-file-validity
807 "Check `file-notify-valid-p' via file notification for remote files.")
809 (ert-deftest file-notify-test05-dir-validity ()
810 "Check `file-notify-valid-p' for directories."
811 (skip-unless (file-notify--test-local-enabled))
813 (unwind-protect
814 (progn
815 (should
816 (setq file-notify--test-tmpfile
817 (make-temp-file "file-notify-test-parent" t)))
818 (should
819 (setq file-notify--test-desc
820 (file-notify-add-watch
821 file-notify--test-tmpfile '(change) #'ignore)))
822 (should (file-notify-valid-p file-notify--test-desc))
823 ;; After removing the watch, the descriptor must not be valid
824 ;; anymore.
825 (file-notify-rm-watch file-notify--test-desc)
826 (file-notify--wait-for-events
827 (file-notify--test-timeout)
828 (not (file-notify-valid-p file-notify--test-desc)))
829 (should-not (file-notify-valid-p file-notify--test-desc))
830 (delete-directory file-notify--test-tmpfile t)
832 ;; The environment shall be cleaned up.
833 (file-notify--test-cleanup-p))
835 ;; Cleanup.
836 (file-notify--test-cleanup))
838 (unwind-protect
839 (progn
840 (should
841 (setq file-notify--test-tmpfile
842 (make-temp-file "file-notify-test-parent" t)))
843 (should
844 (setq file-notify--test-desc
845 (file-notify-add-watch
846 file-notify--test-tmpfile '(change) #'ignore)))
847 (should (file-notify-valid-p file-notify--test-desc))
848 ;; After deleting the directory, the descriptor must not be
849 ;; valid anymore.
850 (delete-directory file-notify--test-tmpfile t)
851 (file-notify--wait-for-events
852 (file-notify--test-timeout)
853 (not (file-notify-valid-p file-notify--test-desc)))
854 (should-not (file-notify-valid-p file-notify--test-desc))
856 ;; The environment shall be cleaned up.
857 (file-notify--test-cleanup-p))
859 ;; Cleanup.
860 (file-notify--test-cleanup)))
862 (file-notify--deftest-remote file-notify-test05-dir-validity
863 "Check `file-notify-valid-p' via file notification for remote directories.")
865 (ert-deftest file-notify-test06-many-events ()
866 "Check that events are not dropped."
867 :tags '(:expensive-test)
868 (skip-unless (file-notify--test-local-enabled))
870 (should
871 (setq file-notify--test-tmpfile
872 (make-temp-file "file-notify-test-parent" t)))
873 (should
874 (setq file-notify--test-desc
875 (file-notify-add-watch
876 file-notify--test-tmpfile
877 '(change) #'file-notify--test-event-handler)))
878 (unwind-protect
879 (let ((n 1000)
880 source-file-list target-file-list
881 (default-directory file-notify--test-tmpfile))
882 (dotimes (i n)
883 ;; It matters which direction we rename, at least for
884 ;; kqueue. This backend parses directories in alphabetic
885 ;; order (x%d before y%d). So we rename into both directions.
886 (if (zerop (mod i 2))
887 (progn
888 (push (expand-file-name (format "x%d" i)) source-file-list)
889 (push (expand-file-name (format "y%d" i)) target-file-list))
890 (push (expand-file-name (format "y%d" i)) source-file-list)
891 (push (expand-file-name (format "x%d" i)) target-file-list)))
892 (file-notify--test-with-events (make-list (+ n n) 'created)
893 (let ((source-file-list source-file-list)
894 (target-file-list target-file-list))
895 (while (and source-file-list target-file-list)
896 (file-notify--test-read-event)
897 (write-region "" nil (pop source-file-list) nil 'no-message)
898 (file-notify--test-read-event)
899 (write-region "" nil (pop target-file-list) nil 'no-message))))
900 (file-notify--test-with-events
901 (cond
902 ;; w32notify fires both `deleted' and `renamed' events.
903 ((string-equal (file-notify--test-library) "w32notify")
904 (let (r)
905 (dotimes (_i n r)
906 (setq r (append '(deleted renamed) r)))))
907 ;; cygwin fires `changed' and `deleted' events, sometimes
908 ;; in random order.
909 ((eq system-type 'cygwin)
910 (let (r)
911 (dotimes (_i n (cons :random r))
912 (setq r (append '(changed deleted) r)))))
913 (t (make-list n 'renamed)))
914 (let ((source-file-list source-file-list)
915 (target-file-list target-file-list))
916 (while (and source-file-list target-file-list)
917 (file-notify--test-read-event)
918 (rename-file (pop source-file-list) (pop target-file-list) t))))
919 (file-notify--test-with-events (make-list n 'deleted)
920 (dolist (file target-file-list)
921 (file-notify--test-read-event)
922 (delete-file file)))
923 (delete-directory file-notify--test-tmpfile)
925 ;; The environment shall be cleaned up.
926 (file-notify--test-cleanup-p))
928 ;; Cleanup.
929 (file-notify--test-cleanup)))
931 (file-notify--deftest-remote file-notify-test06-many-events
932 "Check that events are not dropped for remote directories.")
934 (ert-deftest file-notify-test07-backup ()
935 "Check that backup keeps file notification."
936 (skip-unless (file-notify--test-local-enabled))
938 (unwind-protect
939 (progn
940 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
941 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
942 (should
943 (setq file-notify--test-desc
944 (file-notify-add-watch
945 file-notify--test-tmpfile
946 '(change) #'file-notify--test-event-handler)))
947 (should (file-notify-valid-p file-notify--test-desc))
948 (file-notify--test-with-events
949 ;; There could be one or two `changed' events.
950 '((changed)
951 (changed changed))
952 ;; There shouldn't be any problem, because the file is kept.
953 (with-temp-buffer
954 (let ((buffer-file-name file-notify--test-tmpfile)
955 (make-backup-files t)
956 (backup-by-copying t)
957 (kept-new-versions 1)
958 (delete-old-versions t))
959 (insert "another text")
960 (save-buffer))))
961 ;; After saving the buffer, the descriptor is still valid.
962 (should (file-notify-valid-p file-notify--test-desc))
963 (delete-file file-notify--test-tmpfile)
965 ;; The environment shall be cleaned up.
966 (file-notify--test-cleanup-p))
968 ;; Cleanup.
969 (file-notify--test-cleanup))
971 (unwind-protect
972 ;; It doesn't work for kqueue, because we don't use an implicit
973 ;; directory monitor.
974 (unless (string-equal (file-notify--test-library) "kqueue")
975 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
976 (write-region
977 "any text" nil file-notify--test-tmpfile nil 'no-message)
978 (should
979 (setq file-notify--test-desc
980 (file-notify-add-watch
981 file-notify--test-tmpfile
982 '(change) #'file-notify--test-event-handler)))
983 (should (file-notify-valid-p file-notify--test-desc))
984 (file-notify--test-with-events
985 (cond
986 ;; On cygwin we only get the `changed' event.
987 ((eq system-type 'cygwin) '(changed))
988 (t '(renamed created changed)))
989 ;; The file is renamed when creating a backup. It shall
990 ;; still be watched.
991 (with-temp-buffer
992 (let ((buffer-file-name file-notify--test-tmpfile)
993 (make-backup-files t)
994 (backup-by-copying nil)
995 (backup-by-copying-when-mismatch nil)
996 (kept-new-versions 1)
997 (delete-old-versions t))
998 (insert "another text")
999 (save-buffer))))
1000 ;; After saving the buffer, the descriptor is still valid.
1001 (should (file-notify-valid-p file-notify--test-desc))
1002 (delete-file file-notify--test-tmpfile)
1004 ;; The environment shall be cleaned up.
1005 (file-notify--test-cleanup-p))
1007 ;; Cleanup.
1008 (file-notify--test-cleanup)))
1010 (file-notify--deftest-remote file-notify-test07-backup
1011 "Check that backup keeps file notification for remote files.")
1013 (ert-deftest file-notify-test08-watched-file-in-watched-dir ()
1014 "Watches a directory and a file in that directory separately.
1015 Checks that the callbacks are only called with events with
1016 descriptors that were issued when registering the watches. This
1017 test caters for the situation in bug#22736 where the callback for
1018 the directory received events for the file with the descriptor of
1019 the file watch."
1020 :tags '(:expensive-test)
1021 (skip-unless (file-notify--test-local-enabled))
1023 ;; A directory to be watched.
1024 (should
1025 (setq file-notify--test-tmpfile
1026 (make-temp-file "file-notify-test-parent" t)))
1027 ;; A file to be watched.
1028 (should
1029 (setq file-notify--test-tmpfile1
1030 (let ((temporary-file-directory file-notify--test-tmpfile))
1031 (file-notify--test-make-temp-name))))
1032 (write-region "any text" nil file-notify--test-tmpfile1 nil 'no-message)
1033 (unwind-protect
1034 (cl-flet (;; Directory monitor.
1035 (dir-callback (event)
1036 (let ((file-notify--test-desc file-notify--test-desc1))
1037 (file-notify--test-event-handler event)))
1038 ;; File monitor.
1039 (file-callback (event)
1040 (let ((file-notify--test-desc file-notify--test-desc2))
1041 (file-notify--test-event-handler event))))
1042 (should
1043 (setq file-notify--test-desc1
1044 (file-notify-add-watch
1045 file-notify--test-tmpfile
1046 '(change) #'dir-callback)))
1047 (should
1048 (setq file-notify--test-desc2
1049 (file-notify-add-watch
1050 file-notify--test-tmpfile1
1051 '(change) #'file-callback)))
1052 (should (file-notify-valid-p file-notify--test-desc1))
1053 (should (file-notify-valid-p file-notify--test-desc2))
1054 (should-not (equal file-notify--test-desc1 file-notify--test-desc2))
1055 (let ((n 100))
1056 ;; Run the test.
1057 (file-notify--test-with-events
1058 ;; There could be one or two `changed' events.
1059 (list
1060 ;; cygwin.
1061 (append
1062 '(:random)
1063 (make-list (/ n 2) 'changed)
1064 (make-list (/ n 2) 'created)
1065 (make-list (/ n 2) 'changed))
1066 (append
1067 '(:random)
1068 ;; Directory monitor and file monitor.
1069 (make-list (/ n 2) 'changed)
1070 (make-list (/ n 2) 'changed)
1071 ;; Just the directory monitor.
1072 (make-list (/ n 2) 'created)
1073 (make-list (/ n 2) 'changed))
1074 (append
1075 '(:random)
1076 ;; Directory monitor and file monitor.
1077 (make-list (/ n 2) 'changed)
1078 (make-list (/ n 2) 'changed)
1079 (make-list (/ n 2) 'changed)
1080 (make-list (/ n 2) 'changed)
1081 ;; Just the directory monitor.
1082 (make-list (/ n 2) 'created)
1083 (make-list (/ n 2) 'changed)))
1084 (dotimes (i n)
1085 (file-notify--test-read-event)
1086 (if (zerop (mod i 2))
1087 (write-region
1088 "any text" nil file-notify--test-tmpfile1 t 'no-message)
1089 (let ((temporary-file-directory file-notify--test-tmpfile))
1090 (write-region
1091 "any text" nil
1092 (file-notify--test-make-temp-name) nil 'no-message))))))
1094 ;; If we delete the file, the directory monitor shall still be
1095 ;; active. We receive the `deleted' event from both the
1096 ;; directory and the file monitor. The `stopped' event is
1097 ;; from the file monitor. It's undecided in which order the
1098 ;; the directory and the file monitor are triggered.
1099 (file-notify--test-with-events '(:random deleted deleted stopped)
1100 (delete-file file-notify--test-tmpfile1))
1101 (should (file-notify-valid-p file-notify--test-desc1))
1102 (should-not (file-notify-valid-p file-notify--test-desc2))
1104 ;; Now we delete the directory.
1105 (file-notify--test-with-events
1106 (cond
1107 ;; In kqueue and for cygwin, just one `deleted' event for
1108 ;; the directory is received.
1109 ((or (eq system-type 'cygwin)
1110 (string-equal (file-notify--test-library) "kqueue"))
1111 '(deleted stopped))
1112 (t (append
1113 ;; The directory monitor raises a `deleted' event for
1114 ;; every file contained in the directory, we must
1115 ;; count them.
1116 (make-list
1117 (length
1118 (directory-files
1119 file-notify--test-tmpfile nil
1120 directory-files-no-dot-files-regexp 'nosort))
1121 'deleted)
1122 ;; The events of the directory itself.
1123 (cond
1124 ;; w32notify does not raise `deleted' and `stopped'
1125 ;; events for the watched directory.
1126 ((string-equal (file-notify--test-library) "w32notify") '())
1127 (t '(deleted stopped))))))
1128 (delete-directory file-notify--test-tmpfile 'recursive))
1129 (should-not (file-notify-valid-p file-notify--test-desc1))
1130 (should-not (file-notify-valid-p file-notify--test-desc2))
1132 ;; The environment shall be cleaned up.
1133 (file-notify--test-cleanup-p))
1135 ;; Cleanup.
1136 (file-notify--test-cleanup)))
1138 (file-notify--deftest-remote file-notify-test08-watched-file-in-watched-dir
1139 "Check `file-notify-test08-watched-file-in-watched-dir' for remote files.")
1141 (ert-deftest file-notify-test09-sufficient-resources ()
1142 "Check that file notification does not use too many resources."
1143 :tags '(:expensive-test)
1144 (skip-unless (file-notify--test-local-enabled))
1145 ;; This test is intended for kqueue only.
1146 (skip-unless (string-equal (file-notify--test-library) "kqueue"))
1148 (should
1149 (setq file-notify--test-tmpfile
1150 (make-temp-file "file-notify-test-parent" t)))
1151 (unwind-protect
1152 (let ((temporary-file-directory file-notify--test-tmpfile)
1153 descs)
1154 (should-error
1155 (while t
1156 ;; We watch directories, because we want to reach the upper
1157 ;; limit. Watching a file might not be sufficient, because
1158 ;; most of the libraries implement this as watching the
1159 ;; upper directory.
1160 (setq file-notify--test-tmpfile1
1161 (make-temp-file "file-notify-test-parent" t)
1162 descs
1163 (cons
1164 (should
1165 (file-notify-add-watch
1166 file-notify--test-tmpfile1 '(change) #'ignore))
1167 descs)))
1168 :type 'file-notify-error)
1169 ;; Remove watches. If we don't do it prior removing
1170 ;; directories, Emacs crashes in batch mode.
1171 (dolist (desc descs)
1172 (file-notify-rm-watch desc))
1173 ;; Remove directories.
1174 (delete-directory file-notify--test-tmpfile 'recursive)
1176 ;; The environment shall be cleaned up.
1177 (file-notify--test-cleanup-p))
1179 ;; Cleanup.
1180 (file-notify--test-cleanup)))
1182 (file-notify--deftest-remote file-notify-test09-sufficient-resources
1183 "Check `file-notify-test09-sufficient-resources' for remote files.")
1185 (defun file-notify-test-all (&optional interactive)
1186 "Run all tests for \\[file-notify]."
1187 (interactive "p")
1188 (if interactive
1189 (ert-run-tests-interactively "^file-notify-")
1190 (ert-run-tests-batch "^file-notify-")))
1192 ;; TODO:
1194 ;; * kqueue does not send all expected `deleted' events. Maybe due to
1195 ;; the missing directory monitor.
1196 ;; * For w32notify, no `deleted' and `stopped' events arrive when a
1197 ;; directory is removed.
1198 ;; * For cygwin and w32notify, no `attribute-changed' events arrive.
1199 ;; They send `changed' events instead.
1200 ;; * cygwin does not send all expected `changed' and `deleted' events.
1201 ;; Probably due to timing issues.
1203 (provide 'file-notify-tests)
1204 ;;; filenotify-tests.el ends here