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