]> code.delx.au - gnu-emacs/blob - test/lisp/filenotify-tests.el
Merge from origin/emacs-25
[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 ((eq system-type 'cygwin) 10)
77 (t 3)))
78
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)
84
85 (ignore-errors
86 (delete-file (file-newest-backup file-notify--test-tmpfile)))
87 (ignore-errors
88 (if (file-directory-p file-notify--test-tmpfile)
89 (delete-directory file-notify--test-tmpfile 'recursive)
90 (delete-file file-notify--test-tmpfile)))
91 (ignore-errors
92 (if (file-directory-p file-notify--test-tmpfile1)
93 (delete-directory file-notify--test-tmpfile1 'recursive)
94 (delete-file file-notify--test-tmpfile1)))
95 (ignore-errors
96 (when (file-remote-p temporary-file-directory)
97 (tramp-cleanup-connection
98 (tramp-dissect-file-name temporary-file-directory) nil 'keep-password)))
99
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")))
109
110 (setq password-cache-expiry nil
111 tramp-verbose 0
112 tramp-message-show-message nil)
113
114 ;; This shall happen on hydra only.
115 (when (getenv "NIX_STORE")
116 (add-to-list 'tramp-remote-path 'tramp-own-remote-path))
117
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)))
125
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'
129 being the result.")
130
131 (defun file-notify--test-remote-enabled ()
132 "Whether remote file notification is enabled."
133 (unless (consp file-notify--test-remote-enabled-checked)
134 (let (desc)
135 (ignore-errors
136 (and
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)
140 (setq desc
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))))
146 ;; Return result.
147 (cdr file-notify--test-remote-enabled-checked))
148
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))))))
160
161 (defmacro file-notify--deftest-remote (test docstring)
162 "Define ert `TEST-remote' for remote files."
163 (declare (indent 1))
164 `(ert-deftest ,(intern (concat (symbol-name test) "-remote")) ()
165 ,docstring
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)))))
175
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))
181 (should
182 (setq file-notify--test-desc
183 (file-notify-add-watch temporary-file-directory '(change) #'ignore)))
184
185 ;; Cleanup.
186 (file-notify--test-cleanup))
187
188 (file-notify--deftest-remote file-notify-test00-availability
189 "Test availability of `file-notify' for remote files.")
190
191 (ert-deftest file-notify-test01-add-watch ()
192 "Check `file-notify-add-watch'."
193 (skip-unless (file-notify--test-local-enabled))
194
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))))
198
199 ;; Check, that different valid parameters are accepted.
200 (should
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)
204 (should
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)
209 (should
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)
215 (should
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)
221
222 ;; Check error handling.
223 (should-error (file-notify-add-watch 1 2 3 4)
224 :type 'wrong-number-of-arguments)
225 (should
226 (equal (should-error
227 (file-notify-add-watch 1 2 3))
228 '(wrong-type-argument 1)))
229 (should
230 (equal (should-error
231 (file-notify-add-watch temporary-file-directory 2 3))
232 '(wrong-type-argument 2)))
233 (should
234 (equal (should-error
235 (file-notify-add-watch temporary-file-directory '(change) 3))
236 '(wrong-type-argument 3)))
237 ;; The upper directory of a file must exist.
238 (should
239 (equal (should-error
240 (file-notify-add-watch
241 file-notify--test-tmpfile1 '(change attribute-change) #'ignore))
242 `(file-notify-error
243 "Directory does not exist" ,file-notify--test-tmpfile)))
244
245 ;; Cleanup.
246 (file-notify--test-cleanup))
247
248 (file-notify--deftest-remote file-notify-test01-add-watch
249 "Check `file-notify-add-watch' for remote files.")
250
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'
254 is bound somewhere."
255 ;; Check the descriptor.
256 (should (equal (car file-notify--test-event) file-notify--test-desc))
257 ;; Check the file name.
258 (should
259 (string-prefix-p
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)
264 (should
265 (string-prefix-p
266 (file-notify--event-watched-file file-notify--test-event)
267 (file-notify--event-file1-name file-notify--test-event)))))
268
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)
274 (result
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
278 (regexp-quote ".#")
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))))))
286
287 (defun file-notify--test-make-temp-name ()
288 "Create a temporary file name for test."
289 (expand-file-name
290 (make-temp-name "file-notify-test") temporary-file-directory))
291
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))
296 (while (null ,until)
297 (read-event nil nil file-notify--test-read-event-timeout))))
298
299 (defun file-notify--test-with-events-check (events)
300 "Check whether received events match one of the EVENTS alternatives."
301 (let (result)
302 (dolist (elt events result)
303 (setq result
304 (or result
305 (equal elt (mapcar #'cadr file-notify--test-events)))))))
306
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))
312 (format
313 "Received events `%s' do not match any sequence of expected events `%s'"
314 (mapcar #'cadr file-notify--test-events) events)))
315
316 (put 'file-notify--test-with-events-check 'ert-explainer
317 'file-notify--test-with-events-explainer)
318
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."
324 (declare (indent 1))
325 `(let* ((events (if (consp (car ,events)) ,events (list ,events)))
326 (max-length (apply 'max (mapcar 'length events)))
327 create-lockfiles)
328 ;; Flush pending events.
329 (file-notify--wait-for-events
330 (file-notify--test-timeout)
331 (input-pending-p))
332 (setq file-notify--test-events nil
333 file-notify--test-results nil)
334 ,@body
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
340 ;; are as expected.
341 (dolist (result file-notify--test-results)
342 (when (ert-test-failed-p result)
343 (ert-fail
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))))
347
348 (ert-deftest file-notify-test02-events ()
349 "Check file creation/change/removal notifications."
350 (skip-unless (file-notify--test-local-enabled))
351
352 (unwind-protect
353 (progn
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))
360 (should
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
366 (cond
367 ;; cygwin recognizes only `deleted' and `stopped' events.
368 ((eq system-type 'cygwin)
369 '(deleted stopped))
370 (t '(created changed deleted stopped)))
371 (write-region
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))
376
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)
380 (should
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
386 (cond
387 ;; cygwin recognizes only `deleted' and `stopped' events.
388 ((eq system-type 'cygwin)
389 '(deleted stopped))
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)
401 (write-region
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)
406
407 ;; Check file creation, change and deletion when watching a
408 ;; directory. There must be a `stopped' event when deleting
409 ;; the directory.
410 (let ((temporary-file-directory
411 (make-temp-file "file-notify-test-parent" t)))
412 (should
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
419 (cond
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)
426 '(deleted stopped))
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)
433 (write-region
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 (read-event nil nil file-notify--test-read-event-timeout))
438 (file-notify-rm-watch file-notify--test-desc))
439
440 ;; Check copy of files inside a directory.
441 (let ((temporary-file-directory
442 (make-temp-file "file-notify-test-parent" t)))
443 (should
444 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
445 file-notify--test-tmpfile1 (file-notify--test-make-temp-name)
446 file-notify--test-desc
447 (file-notify-add-watch
448 temporary-file-directory
449 '(change) #'file-notify--test-event-handler)))
450 (file-notify--test-with-events
451 (cond
452 ;; w32notify does not distinguish between `changed' and
453 ;; `attribute-changed'. It does not raise `deleted'
454 ;; and `stopped' events for the watched directory.
455 ((string-equal (file-notify--test-library) "w32notify")
456 '(created changed created changed
457 changed changed changed
458 deleted deleted))
459 ;; cygwin recognizes only `deleted' and `stopped' events.
460 ((eq system-type 'cygwin)
461 '(deleted stopped))
462 ;; There are three `deleted' events, for two files and
463 ;; for the directory. Except for kqueue.
464 ((string-equal (file-notify--test-library) "kqueue")
465 '(created changed created changed deleted stopped))
466 (t '(created changed created changed
467 deleted deleted deleted stopped)))
468 (read-event nil nil file-notify--test-read-event-timeout)
469 (write-region
470 "any text" nil file-notify--test-tmpfile nil 'no-message)
471 (read-event nil nil file-notify--test-read-event-timeout)
472 (copy-file file-notify--test-tmpfile file-notify--test-tmpfile1)
473 ;; The next two events shall not be visible.
474 (read-event nil nil file-notify--test-read-event-timeout)
475 (set-file-modes file-notify--test-tmpfile 000)
476 (read-event nil nil file-notify--test-read-event-timeout)
477 (set-file-times file-notify--test-tmpfile '(0 0))
478 (read-event nil nil file-notify--test-read-event-timeout)
479 (delete-directory temporary-file-directory 'recursive)
480 (read-event nil nil file-notify--test-read-event-timeout))
481 (file-notify-rm-watch file-notify--test-desc))
482
483 ;; Check rename of files inside a directory.
484 (let ((temporary-file-directory
485 (make-temp-file "file-notify-test-parent" t)))
486 (should
487 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
488 file-notify--test-tmpfile1 (file-notify--test-make-temp-name)
489 file-notify--test-desc
490 (file-notify-add-watch
491 temporary-file-directory
492 '(change) #'file-notify--test-event-handler)))
493 (file-notify--test-with-events
494 (cond
495 ;; w32notify does not raise `deleted' and `stopped'
496 ;; events for the watched directory.
497 ((string-equal (file-notify--test-library) "w32notify")
498 '(created changed renamed deleted))
499 ;; cygwin recognizes only `deleted' and `stopped' events.
500 ((eq system-type 'cygwin)
501 '(deleted stopped))
502 ;; There are two `deleted' events, for the file and for
503 ;; the directory. Except for kqueue.
504 ((string-equal (file-notify--test-library) "kqueue")
505 '(created changed renamed deleted stopped))
506 (t '(created changed renamed deleted deleted stopped)))
507 (read-event nil nil file-notify--test-read-event-timeout)
508 (write-region
509 "any text" nil file-notify--test-tmpfile nil 'no-message)
510 (read-event nil nil file-notify--test-read-event-timeout)
511 (rename-file file-notify--test-tmpfile file-notify--test-tmpfile1)
512 ;; After the rename, we won't get events anymore.
513 (read-event nil nil file-notify--test-read-event-timeout)
514 (delete-directory temporary-file-directory 'recursive)
515 (read-event nil nil file-notify--test-read-event-timeout))
516 (file-notify-rm-watch file-notify--test-desc))
517
518 ;; Check attribute change. Does not work for cygwin.
519 (unless (eq system-type 'cygwin)
520 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
521 (write-region
522 "any text" nil file-notify--test-tmpfile nil 'no-message)
523 (should
524 (setq file-notify--test-desc
525 (file-notify-add-watch
526 file-notify--test-tmpfile
527 '(attribute-change) #'file-notify--test-event-handler)))
528 (file-notify--test-with-events
529 (cond
530 ;; w32notify does not distinguish between `changed' and
531 ;; `attribute-changed'.
532 ((string-equal (file-notify--test-library) "w32notify")
533 '(changed changed))
534 ;; For kqueue and in the remote case, `write-region'
535 ;; raises also an `attribute-changed' event.
536 ((or (string-equal (file-notify--test-library) "kqueue")
537 (file-remote-p temporary-file-directory))
538 '(attribute-changed attribute-changed attribute-changed))
539 (t '(attribute-changed attribute-changed)))
540 (read-event nil nil file-notify--test-read-event-timeout)
541 (write-region
542 "any text" nil file-notify--test-tmpfile nil 'no-message)
543 (read-event nil nil file-notify--test-read-event-timeout)
544 (set-file-modes file-notify--test-tmpfile 000)
545 (read-event nil nil file-notify--test-read-event-timeout)
546 (set-file-times file-notify--test-tmpfile '(0 0))
547 (read-event nil nil file-notify--test-read-event-timeout)
548 (delete-file file-notify--test-tmpfile))
549 (file-notify-rm-watch file-notify--test-desc)))
550
551 ;; Cleanup.
552 (file-notify--test-cleanup)))
553
554 (file-notify--deftest-remote file-notify-test02-events
555 "Check file creation/change/removal notifications for remote files.")
556
557 (require 'autorevert)
558 (setq auto-revert-notify-exclude-dir-regexp "nothing-to-be-excluded"
559 auto-revert-remote-files t
560 auto-revert-stop-on-user-input nil)
561
562 (ert-deftest file-notify-test03-autorevert ()
563 "Check autorevert via file notification."
564 (skip-unless (file-notify--test-local-enabled))
565 ;; `auto-revert-buffers' runs every 5". And we must wait, until the
566 ;; file has been reverted.
567 (let ((timeout (if (file-remote-p temporary-file-directory) 60 10))
568 buf)
569 (unwind-protect
570 (progn
571 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
572
573 (write-region
574 "any text" nil file-notify--test-tmpfile nil 'no-message)
575 (setq buf (find-file-noselect file-notify--test-tmpfile))
576 (with-current-buffer buf
577 (should (string-equal (buffer-string) "any text"))
578 ;; `buffer-stale--default-function' checks for
579 ;; `verify-visited-file-modtime'. We must ensure that it
580 ;; returns nil.
581 (sleep-for 1)
582 (auto-revert-mode 1)
583
584 ;; `auto-revert-buffers' runs every 5".
585 (with-timeout (timeout (ignore))
586 (while (null auto-revert-notify-watch-descriptor)
587 (sleep-for 1)))
588
589 ;; Check, that file notification has been used.
590 (should auto-revert-mode)
591 (should auto-revert-use-notify)
592 (should auto-revert-notify-watch-descriptor)
593
594 ;; Modify file. We wait for a second, in order to have
595 ;; another timestamp.
596 (with-current-buffer (get-buffer-create "*Messages*")
597 (narrow-to-region (point-max) (point-max)))
598 (sleep-for 1)
599 (write-region
600 "another text" nil file-notify--test-tmpfile nil 'no-message)
601
602 ;; Check, that the buffer has been reverted.
603 (with-current-buffer (get-buffer-create "*Messages*")
604 (file-notify--wait-for-events
605 timeout
606 (string-match
607 (format-message "Reverting buffer `%s'." (buffer-name buf))
608 (buffer-string))))
609 (should (string-match "another text" (buffer-string)))
610
611 ;; Stop file notification. Autorevert shall still work via polling.
612 (file-notify-rm-watch auto-revert-notify-watch-descriptor)
613 (file-notify--wait-for-events
614 timeout (null auto-revert-use-notify))
615 (should-not auto-revert-use-notify)
616 (should-not auto-revert-notify-watch-descriptor)
617
618 ;; Modify file. We wait for two seconds, in order to
619 ;; have another timestamp. One second seems to be too
620 ;; short.
621 (with-current-buffer (get-buffer-create "*Messages*")
622 (narrow-to-region (point-max) (point-max)))
623 (sleep-for 2)
624 (write-region
625 "foo bla" nil file-notify--test-tmpfile nil 'no-message)
626
627 ;; Check, that the buffer has been reverted.
628 (with-current-buffer (get-buffer-create "*Messages*")
629 (file-notify--wait-for-events
630 timeout
631 (string-match
632 (format-message "Reverting buffer `%s'." (buffer-name buf))
633 (buffer-string))))
634 (should (string-match "foo bla" (buffer-string)))))
635
636 ;; Cleanup.
637 (with-current-buffer "*Messages*" (widen))
638 (ignore-errors (kill-buffer buf))
639 (file-notify--test-cleanup))))
640
641 (file-notify--deftest-remote file-notify-test03-autorevert
642 "Check autorevert via file notification for remote files.")
643
644 (ert-deftest file-notify-test04-file-validity ()
645 "Check `file-notify-valid-p' for files."
646 (skip-unless (file-notify--test-local-enabled))
647
648 (unwind-protect
649 (progn
650 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
651 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
652 (should
653 (setq file-notify--test-desc
654 (file-notify-add-watch
655 file-notify--test-tmpfile
656 '(change) #'file-notify--test-event-handler)))
657 (should (file-notify-valid-p file-notify--test-desc))
658 ;; After calling `file-notify-rm-watch', the descriptor is not
659 ;; valid anymore.
660 (file-notify-rm-watch file-notify--test-desc)
661 (should-not (file-notify-valid-p file-notify--test-desc))
662 (delete-file file-notify--test-tmpfile))
663
664 ;; Cleanup.
665 (file-notify--test-cleanup))
666
667 (unwind-protect
668 (progn
669 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
670 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
671 (should
672 (setq file-notify--test-desc
673 (file-notify-add-watch
674 file-notify--test-tmpfile
675 '(change) #'file-notify--test-event-handler)))
676 (file-notify--test-with-events
677 (cond
678 ;; cygwin recognizes only `deleted' and `stopped' events.
679 ((eq system-type 'cygwin)
680 '(deleted stopped))
681 ;; inotify and kqueue raise just one `changed' event.
682 ((or (string-equal "inotify" (file-notify--test-library))
683 (string-equal "kqueue" (file-notify--test-library)))
684 '(changed deleted stopped))
685 ;; gfilenotify raises one or two `changed' events
686 ;; randomly, no chance to test. So we accept both cases.
687 ((string-equal "gfilenotify" (file-notify--test-library))
688 '((changed deleted stopped)
689 (changed changed deleted stopped)))
690 (t '(changed changed deleted stopped)))
691 (should (file-notify-valid-p file-notify--test-desc))
692 (read-event nil nil file-notify--test-read-event-timeout)
693 (write-region
694 "another text" nil file-notify--test-tmpfile nil 'no-message)
695 (read-event nil nil file-notify--test-read-event-timeout)
696 (delete-file file-notify--test-tmpfile))
697 ;; After deleting the file, the descriptor is not valid anymore.
698 (should-not (file-notify-valid-p file-notify--test-desc))
699 (file-notify-rm-watch file-notify--test-desc))
700
701 ;; Cleanup.
702 (file-notify--test-cleanup))
703
704 (unwind-protect
705 (let ((temporary-file-directory
706 (make-temp-file "file-notify-test-parent" t)))
707 (should
708 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
709 file-notify--test-desc
710 (file-notify-add-watch
711 temporary-file-directory
712 '(change) #'file-notify--test-event-handler)))
713 (file-notify--test-with-events
714 (cond
715 ;; w32notify does not raise `deleted' and `stopped' events
716 ;; for the watched directory.
717 ((string-equal (file-notify--test-library) "w32notify")
718 '(created changed deleted))
719 ;; cygwin recognizes only `deleted' and `stopped' events.
720 ((eq system-type 'cygwin)
721 '(deleted stopped))
722 ;; There are two `deleted' events, for the file and for the
723 ;; directory. Except for kqueue.
724 ((string-equal (file-notify--test-library) "kqueue")
725 '(created changed deleted stopped))
726 (t '(created changed deleted deleted stopped)))
727 (should (file-notify-valid-p file-notify--test-desc))
728 (read-event nil nil file-notify--test-read-event-timeout)
729 (write-region
730 "any text" nil file-notify--test-tmpfile nil 'no-message)
731 (read-event nil nil file-notify--test-read-event-timeout)
732 (delete-directory temporary-file-directory t))
733 ;; After deleting the parent directory, the descriptor must
734 ;; not be valid anymore.
735 (should-not (file-notify-valid-p file-notify--test-desc)))
736
737 ;; Cleanup.
738 (file-notify--test-cleanup)))
739
740 (file-notify--deftest-remote file-notify-test04-file-validity
741 "Check `file-notify-valid-p' via file notification for remote files.")
742
743 (ert-deftest file-notify-test05-dir-validity ()
744 "Check `file-notify-valid-p' for directories."
745 (skip-unless (file-notify--test-local-enabled))
746
747 (unwind-protect
748 (progn
749 (should
750 (setq file-notify--test-tmpfile
751 (make-temp-file "file-notify-test-parent" t)))
752 (should
753 (setq file-notify--test-desc
754 (file-notify-add-watch
755 file-notify--test-tmpfile
756 '(change) #'file-notify--test-event-handler)))
757 (should (file-notify-valid-p file-notify--test-desc))
758 ;; After removing the watch, the descriptor must not be valid
759 ;; anymore.
760 (read-event nil nil file-notify--test-read-event-timeout)
761 (file-notify-rm-watch file-notify--test-desc)
762 (read-event nil nil file-notify--test-read-event-timeout)
763 (file-notify--wait-for-events
764 (file-notify--test-timeout)
765 (not (file-notify-valid-p file-notify--test-desc)))
766 (should-not (file-notify-valid-p file-notify--test-desc)))
767
768 ;; Cleanup.
769 (file-notify--test-cleanup))
770
771 (unwind-protect
772 (progn
773 (should
774 (setq file-notify--test-tmpfile
775 (make-temp-file "file-notify-test-parent" t)))
776 (should
777 (setq file-notify--test-desc
778 (file-notify-add-watch
779 file-notify--test-tmpfile
780 '(change) #'file-notify--test-event-handler)))
781 (should (file-notify-valid-p file-notify--test-desc))
782 ;; After deleting the directory, the descriptor must not be
783 ;; valid anymore.
784 (read-event nil nil file-notify--test-read-event-timeout)
785 (delete-directory file-notify--test-tmpfile t)
786 (read-event nil nil file-notify--test-read-event-timeout)
787 (file-notify--wait-for-events
788 (file-notify--test-timeout)
789 (not (file-notify-valid-p file-notify--test-desc)))
790 (should-not (file-notify-valid-p file-notify--test-desc)))
791
792 ;; Cleanup.
793 (file-notify--test-cleanup)))
794
795 (file-notify--deftest-remote file-notify-test05-dir-validity
796 "Check `file-notify-valid-p' via file notification for remote directories.")
797
798 (ert-deftest file-notify-test06-many-events ()
799 "Check that events are not dropped."
800 :tags '(:expensive-test)
801 (skip-unless (file-notify--test-local-enabled))
802 ;; Under cygwin events arrive in random order. Impossible to define a test.
803 (skip-unless (not (eq system-type 'cygwin)))
804
805 (should
806 (setq file-notify--test-tmpfile
807 (make-temp-file "file-notify-test-parent" t)))
808 (should
809 (setq file-notify--test-desc
810 (file-notify-add-watch
811 file-notify--test-tmpfile
812 '(change) #'file-notify--test-event-handler)))
813 (unwind-protect
814 (let ((n 1000)
815 source-file-list target-file-list
816 (default-directory file-notify--test-tmpfile))
817 (dotimes (i n)
818 ;; It matters which direction we rename, at least for
819 ;; kqueue. This backend parses directories in alphabetic
820 ;; order (x%d before y%d). So we rename into both directions.
821 (if (zerop (mod i 2))
822 (progn
823 (push (expand-file-name (format "x%d" i)) source-file-list)
824 (push (expand-file-name (format "y%d" i)) target-file-list))
825 (push (expand-file-name (format "y%d" i)) source-file-list)
826 (push (expand-file-name (format "x%d" i)) target-file-list)))
827 (file-notify--test-with-events (make-list (+ n n) 'created)
828 (let ((source-file-list source-file-list)
829 (target-file-list target-file-list))
830 (while (and source-file-list target-file-list)
831 (read-event nil nil file-notify--test-read-event-timeout)
832 (write-region "" nil (pop source-file-list) nil 'no-message)
833 (read-event nil nil file-notify--test-read-event-timeout)
834 (write-region "" nil (pop target-file-list) nil 'no-message))))
835 (file-notify--test-with-events
836 (cond
837 ;; w32notify fires both `deleted' and `renamed' events.
838 ((string-equal (file-notify--test-library) "w32notify")
839 (let (r)
840 (dotimes (_i n r)
841 (setq r (append '(deleted renamed) r)))))
842 (t (make-list n 'renamed)))
843 (let ((source-file-list source-file-list)
844 (target-file-list target-file-list))
845 (while (and source-file-list target-file-list)
846 (read-event nil nil file-notify--test-read-event-timeout)
847 (rename-file (pop source-file-list) (pop target-file-list) t))))
848 (file-notify--test-with-events (make-list n 'deleted)
849 (dolist (file target-file-list)
850 (read-event nil nil file-notify--test-read-event-timeout)
851 (delete-file file) file-notify--test-read-event-timeout)))
852
853 ;; Cleanup.
854 (file-notify--test-cleanup)))
855
856 (file-notify--deftest-remote file-notify-test06-many-events
857 "Check that events are not dropped for remote directories.")
858
859 (ert-deftest file-notify-test07-backup ()
860 "Check that backup keeps file notification."
861 (skip-unless (file-notify--test-local-enabled))
862
863 (unwind-protect
864 (progn
865 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
866 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
867 (should
868 (setq file-notify--test-desc
869 (file-notify-add-watch
870 file-notify--test-tmpfile
871 '(change) #'file-notify--test-event-handler)))
872 (should (file-notify-valid-p file-notify--test-desc))
873 (file-notify--test-with-events
874 (cond
875 ;; For w32notify and in the remote case, there are two
876 ;; `changed' events.
877 ((or (string-equal (file-notify--test-library) "w32notify")
878 (file-remote-p temporary-file-directory))
879 '(changed changed))
880 ;; gfilenotify raises one or two `changed' events
881 ;; randomly, no chance to test. So we accept both cases.
882 ((string-equal "gfilenotify" (file-notify--test-library))
883 '((changed)
884 (changed changed)))
885 (t '(changed)))
886 ;; There shouldn't be any problem, because the file is kept.
887 (with-temp-buffer
888 (let ((buffer-file-name file-notify--test-tmpfile)
889 (make-backup-files t)
890 (backup-by-copying t)
891 (kept-new-versions 1)
892 (delete-old-versions t))
893 (insert "another text")
894 (save-buffer))))
895 ;; After saving the buffer, the descriptor is still valid.
896 (should (file-notify-valid-p file-notify--test-desc))
897 (delete-file file-notify--test-tmpfile))
898
899 ;; Cleanup.
900 (file-notify--test-cleanup))
901
902 (unwind-protect
903 (progn
904 ;; It doesn't work for kqueue, because we don't use an
905 ;; implicit directory monitor.
906 (unless (string-equal (file-notify--test-library) "kqueue")
907 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
908 (write-region
909 "any text" nil file-notify--test-tmpfile nil 'no-message)
910 (should
911 (setq file-notify--test-desc
912 (file-notify-add-watch
913 file-notify--test-tmpfile
914 '(change) #'file-notify--test-event-handler)))
915 (should (file-notify-valid-p file-notify--test-desc))
916 (file-notify--test-with-events '(renamed created changed)
917 ;; The file is renamed when creating a backup. It shall
918 ;; still be watched.
919 (with-temp-buffer
920 (let ((buffer-file-name file-notify--test-tmpfile)
921 (make-backup-files t)
922 (backup-by-copying nil)
923 (backup-by-copying-when-mismatch nil)
924 (kept-new-versions 1)
925 (delete-old-versions t))
926 (insert "another text")
927 (save-buffer))))
928 ;; After saving the buffer, the descriptor is still valid.
929 (should (file-notify-valid-p file-notify--test-desc))
930 (delete-file file-notify--test-tmpfile)))
931
932 ;; Cleanup.
933 (file-notify--test-cleanup)))
934
935 (file-notify--deftest-remote file-notify-test07-backup
936 "Check that backup keeps file notification for remote files.")
937
938 (ert-deftest file-notify-test08-watched-file-in-watched-dir ()
939 "Watches a directory and a file in that directory separately.
940 Checks that the callbacks are only called with events with
941 descriptors that were issued when registering the watches. This
942 test caters for the situation in bug#22736 where the callback for
943 the directory received events for the file with the descriptor of
944 the file watch."
945 :tags '(:expensive-test)
946 (skip-unless (file-notify--test-local-enabled))
947
948 ;; A directory to be watched.
949 (should
950 (setq file-notify--test-tmpfile
951 (make-temp-file "file-notify-test-parent" t)))
952 ;; A file to be watched.
953 (should
954 (setq file-notify--test-tmpfile1
955 (let ((temporary-file-directory file-notify--test-tmpfile))
956 (file-notify--test-make-temp-name))))
957 (write-region "any text" nil file-notify--test-tmpfile1 nil 'no-message)
958 (unwind-protect
959 (cl-flet (;; Directory monitor.
960 (dir-callback (event)
961 (let ((file-notify--test-desc file-notify--test-desc1))
962 (file-notify--test-event-handler event)))
963 ;; File monitor.
964 (file-callback (event)
965 (let ((file-notify--test-desc file-notify--test-desc2))
966 (file-notify--test-event-handler event))))
967 (should
968 (setq file-notify--test-desc1
969 (file-notify-add-watch
970 file-notify--test-tmpfile
971 '(change) #'dir-callback)))
972 (should
973 (setq file-notify--test-desc2
974 (file-notify-add-watch
975 file-notify--test-tmpfile1
976 '(change) #'file-callback)))
977 (should (file-notify-valid-p file-notify--test-desc1))
978 (should (file-notify-valid-p file-notify--test-desc2))
979 (should-not (equal file-notify--test-desc1 file-notify--test-desc2))
980 ;; gfilenotify raises one or two `changed' events randomly in
981 ;; the file monitor, no chance to test.
982 (unless (string-equal "gfilenotify" (file-notify--test-library))
983 (let ((n 100) events)
984 ;; Compute the expected events.
985 (dotimes (_i (/ n 2))
986 (setq events
987 (append
988 (append
989 ;; Directory monitor and file monitor.
990 (cond
991 ;; In the remote case, there are two `changed'
992 ;; events.
993 ((file-remote-p temporary-file-directory)
994 '(changed changed changed changed))
995 ;; The directory monitor in kqueue does not
996 ;; raise any `changed' event. Just the file
997 ;; monitor event is received.
998 ((string-equal (file-notify--test-library) "kqueue")
999 '(changed))
1000 ;; Otherwise, both monitors report the
1001 ;; `changed' event.
1002 (t '(changed changed)))
1003 ;; Just the directory monitor.
1004 (cond
1005 ;; In kqueue, there is an additional `changed'
1006 ;; event. Why?
1007 ((string-equal (file-notify--test-library) "kqueue")
1008 '(changed created changed))
1009 (t '(created changed))))
1010 events)))
1011
1012 ;; Run the test.
1013 (file-notify--test-with-events events
1014 (dotimes (i n)
1015 (read-event nil nil file-notify--test-read-event-timeout)
1016 (if (zerop (mod i 2))
1017 (write-region
1018 "any text" nil file-notify--test-tmpfile1 t 'no-message)
1019 (let ((temporary-file-directory file-notify--test-tmpfile))
1020 (write-region
1021 "any text" nil
1022 (file-notify--test-make-temp-name) nil 'no-message)))))))
1023
1024 ;; If we delete the file, the directory monitor shall still be
1025 ;; active. We receive the `deleted' event from both the
1026 ;; directory and the file monitor. The `stopped' event is
1027 ;; from the file monitor. It's undecided in which order the
1028 ;; the directory and the file monitor are triggered.
1029 (file-notify--test-with-events
1030 '((deleted deleted stopped)
1031 (deleted stopped deleted))
1032 (delete-file file-notify--test-tmpfile1))
1033 (should (file-notify-valid-p file-notify--test-desc1))
1034 (should-not (file-notify-valid-p file-notify--test-desc2))
1035
1036 ;; Now we delete the directory.
1037 (file-notify--test-with-events
1038 (cond
1039 ;; In kqueue, just one `deleted' event for the directory
1040 ;; is received.
1041 ((string-equal (file-notify--test-library) "kqueue")
1042 '(deleted stopped))
1043 (t (append
1044 ;; The directory monitor raises a `deleted' event for
1045 ;; every file contained in the directory, we must
1046 ;; count them.
1047 (make-list
1048 (length
1049 (directory-files
1050 file-notify--test-tmpfile nil
1051 directory-files-no-dot-files-regexp 'nosort))
1052 'deleted)
1053 ;; The events of the directory itself.
1054 (cond
1055 ;; w32notify does not raise `deleted' and `stopped'
1056 ;; events for the watched directory.
1057 ((string-equal (file-notify--test-library) "w32notify") '())
1058 (t '(deleted stopped))))))
1059 (delete-directory file-notify--test-tmpfile 'recursive))
1060 (should-not (file-notify-valid-p file-notify--test-desc1))
1061 (should-not (file-notify-valid-p file-notify--test-desc2)))
1062
1063 ;; Cleanup.
1064 (file-notify--test-cleanup)))
1065
1066 (file-notify--deftest-remote file-notify-test08-watched-file-in-watched-dir
1067 "Check `file-notify-test08-watched-file-in-watched-dir' for remote files.")
1068
1069 (ert-deftest file-notify-test09-sufficient-ressources ()
1070 "Check that file notification does not use too many ressources."
1071 :tags '(:expensive-test)
1072 (skip-unless (file-notify--test-local-enabled))
1073 ;; This test is intended for kqueue only.
1074 (skip-unless (string-equal (file-notify--test-library) "kqueue"))
1075
1076 (should
1077 (setq file-notify--test-tmpfile
1078 (make-temp-file "file-notify-test-parent" t)))
1079 (unwind-protect
1080 (let ((temporary-file-directory file-notify--test-tmpfile)
1081 descs)
1082 (should-error
1083 (while t
1084 ;; We watch directories, because we want to reach the upper
1085 ;; limit. Watching a file might not be sufficient, because
1086 ;; most of the libraries implement this as watching the
1087 ;; upper directory.
1088 (setq file-notify--test-tmpfile1
1089 (make-temp-file "file-notify-test-parent" t)
1090 descs
1091 (cons
1092 (should
1093 (file-notify-add-watch
1094 file-notify--test-tmpfile1 '(change) #'ignore))
1095 descs)))
1096 :type 'file-notify-error)
1097 ;; Remove watches. If we don't do it prior removing
1098 ;; directories, Emacs crashes in batch mode.
1099 (dolist (desc descs)
1100 (file-notify-rm-watch desc))
1101 ;; Remove directories.
1102 (delete-directory file-notify--test-tmpfile 'recursive))
1103
1104 ;; Cleanup.
1105 (file-notify--test-cleanup)))
1106
1107 (file-notify--deftest-remote file-notify-test09-sufficient-ressources
1108 "Check `file-notify-test09-sufficient-ressources' for remote files.")
1109
1110 (defun file-notify-test-all (&optional interactive)
1111 "Run all tests for \\[file-notify]."
1112 (interactive "p")
1113 (if interactive
1114 (ert-run-tests-interactively "^file-notify-")
1115 (ert-run-tests-batch "^file-notify-")))
1116
1117 ;; TODO:
1118
1119 ;; * kqueue does not send all expected `deleted' events. Maybe due to
1120 ;; the missing directory monitor.
1121 ;; * For w32notify, no `deleted' and `stopped' events arrive when a
1122 ;; directory is removed.
1123 ;; * For w32notify, no `attribute-changed' events arrive. Its sends
1124 ;; `changed' events instead.
1125 ;; * Check, why cygwin recognizes only `deleted' and `stopped' events.
1126
1127 (provide 'file-notify-tests)
1128 ;;; file-notify-tests.el ends here