]> 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-results nil)
62 (defvar file-notify--test-event nil)
63 (defvar file-notify--test-events nil)
64
65 (defun file-notify--test-timeout ()
66 "Timeout to wait for arriving events, in seconds."
67 (cond
68 ((file-remote-p temporary-file-directory) 6)
69 ((string-equal (file-notify--test-library) "w32notify") 10)
70 ((eq system-type 'cygwin) 10)
71 (t 3)))
72
73 (defun file-notify--test-cleanup ()
74 "Cleanup after a test."
75 (file-notify-rm-watch file-notify--test-desc)
76
77 (when (and file-notify--test-tmpfile
78 (file-exists-p file-notify--test-tmpfile))
79 (if (file-directory-p file-notify--test-tmpfile)
80 (delete-directory file-notify--test-tmpfile 'recursive)
81 (delete-file file-notify--test-tmpfile)))
82 (when (and file-notify--test-tmpfile1
83 (file-exists-p file-notify--test-tmpfile1))
84 (if (file-directory-p file-notify--test-tmpfile1)
85 (delete-directory file-notify--test-tmpfile1 'recursive)
86 (delete-file file-notify--test-tmpfile1)))
87 (when (file-remote-p temporary-file-directory)
88 (tramp-cleanup-connection
89 (tramp-dissect-file-name temporary-file-directory) nil 'keep-password))
90
91 (setq file-notify--test-tmpfile nil
92 file-notify--test-tmpfile1 nil
93 file-notify--test-desc nil
94 file-notify--test-results nil
95 file-notify--test-events nil)
96 (when file-notify--test-event
97 (error "file-notify--test-event should not be set but bound dynamically")))
98
99 (setq password-cache-expiry nil
100 tramp-verbose 0
101 tramp-message-show-message nil)
102
103 ;; This shall happen on hydra only.
104 (when (getenv "NIX_STORE")
105 (add-to-list 'tramp-remote-path 'tramp-own-remote-path))
106
107 ;; We do not want to try and fail `file-notify-add-watch'.
108 (defun file-notify--test-local-enabled ()
109 "Whether local file notification is enabled.
110 This is needed for local `temporary-file-directory' only, in the
111 remote case we return always t."
112 (or file-notify--library
113 (file-remote-p temporary-file-directory)))
114
115 (defvar file-notify--test-remote-enabled-checked nil
116 "Cached result of `file-notify--test-remote-enabled'.
117 If the function did run, the value is a cons cell, the `cdr'
118 being the result.")
119
120 (defun file-notify--test-remote-enabled ()
121 "Whether remote file notification is enabled."
122 (unless (consp file-notify--test-remote-enabled-checked)
123 (let (desc)
124 (ignore-errors
125 (and
126 (file-remote-p file-notify-test-remote-temporary-file-directory)
127 (file-directory-p file-notify-test-remote-temporary-file-directory)
128 (file-writable-p file-notify-test-remote-temporary-file-directory)
129 (setq desc
130 (file-notify-add-watch
131 file-notify-test-remote-temporary-file-directory
132 '(change) 'ignore))))
133 (setq file-notify--test-remote-enabled-checked (cons t desc))
134 (when desc (file-notify-rm-watch desc))))
135 ;; Return result.
136 (cdr file-notify--test-remote-enabled-checked))
137
138 (defun file-notify--test-library ()
139 "The used library for the test, as a string.
140 In the remote case, it is the process name which runs on the
141 remote host, or nil."
142 (if (null (file-remote-p temporary-file-directory))
143 (symbol-name file-notify--library)
144 (and (consp file-notify--test-remote-enabled-checked)
145 (processp (cdr file-notify--test-remote-enabled-checked))
146 (replace-regexp-in-string
147 "<[[:digit:]]+>\\'" ""
148 (process-name (cdr file-notify--test-remote-enabled-checked))))))
149
150 (defmacro file-notify--deftest-remote (test docstring)
151 "Define ert `TEST-remote' for remote files."
152 (declare (indent 1))
153 `(ert-deftest ,(intern (concat (symbol-name test) "-remote")) ()
154 ,docstring
155 :tags '(:expensive-test)
156 (let* ((temporary-file-directory
157 file-notify-test-remote-temporary-file-directory)
158 (ert-test (ert-get-test ',test)))
159 (skip-unless (file-notify--test-remote-enabled))
160 (tramp-cleanup-connection
161 (tramp-dissect-file-name temporary-file-directory) nil 'keep-password)
162 (funcall (ert-test-body ert-test)))))
163
164 (ert-deftest file-notify-test00-availability ()
165 "Test availability of `file-notify'."
166 (skip-unless (file-notify--test-local-enabled))
167 ;; Report the native library which has been used.
168 (message "Library: `%s'" (file-notify--test-library))
169 (should
170 (setq file-notify--test-desc
171 (file-notify-add-watch temporary-file-directory '(change) 'ignore)))
172
173 ;; Cleanup.
174 (file-notify--test-cleanup))
175
176 (file-notify--deftest-remote file-notify-test00-availability
177 "Test availability of `file-notify' for remote files.")
178
179 (ert-deftest file-notify-test01-add-watch ()
180 "Check `file-notify-add-watch'."
181 (skip-unless (file-notify--test-local-enabled))
182
183 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
184 file-notify--test-tmpfile1
185 (format "%s/%s" file-notify--test-tmpfile (md5 (current-time-string))))
186
187 ;; Check, that different valid parameters are accepted.
188 (should
189 (setq file-notify--test-desc
190 (file-notify-add-watch temporary-file-directory '(change) 'ignore)))
191 (file-notify-rm-watch file-notify--test-desc)
192 (should
193 (setq file-notify--test-desc
194 (file-notify-add-watch
195 temporary-file-directory '(attribute-change) 'ignore)))
196 (file-notify-rm-watch file-notify--test-desc)
197 (should
198 (setq file-notify--test-desc
199 (file-notify-add-watch
200 temporary-file-directory '(change attribute-change) 'ignore)))
201 (file-notify-rm-watch file-notify--test-desc)
202 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
203 (should
204 (setq file-notify--test-desc
205 (file-notify-add-watch
206 file-notify--test-tmpfile '(change attribute-change) 'ignore)))
207 (file-notify-rm-watch file-notify--test-desc)
208 (delete-file file-notify--test-tmpfile)
209
210 ;; Check error handling.
211 (should-error (file-notify-add-watch 1 2 3 4)
212 :type 'wrong-number-of-arguments)
213 (should
214 (equal (should-error
215 (file-notify-add-watch 1 2 3))
216 '(wrong-type-argument 1)))
217 (should
218 (equal (should-error
219 (file-notify-add-watch temporary-file-directory 2 3))
220 '(wrong-type-argument 2)))
221 (should
222 (equal (should-error
223 (file-notify-add-watch temporary-file-directory '(change) 3))
224 '(wrong-type-argument 3)))
225 ;; The upper directory of a file must exist.
226 (should
227 (equal (should-error
228 (file-notify-add-watch
229 file-notify--test-tmpfile1 '(change attribute-change) 'ignore))
230 `(file-notify-error
231 "Directory does not exist" ,file-notify--test-tmpfile)))
232
233 ;; Cleanup.
234 (file-notify--test-cleanup))
235
236 (file-notify--deftest-remote file-notify-test01-add-watch
237 "Check `file-notify-add-watch' for remote files.")
238
239 (defun file-notify--test-event-test ()
240 "Ert test function to be called by `file-notify--test-event-handler'.
241 We cannot pass arguments, so we assume that `file-notify--test-event'
242 is bound somewhere."
243 ;; Check the descriptor.
244 (should (equal (car file-notify--test-event) file-notify--test-desc))
245 ;; Check the file name.
246 (should
247 (or (string-equal (file-notify--event-file-name file-notify--test-event)
248 file-notify--test-tmpfile)
249 (string-equal (file-notify--event-file-name file-notify--test-event)
250 file-notify--test-tmpfile1)
251 (string-equal (file-notify--event-file-name file-notify--test-event)
252 temporary-file-directory)))
253 ;; Check the second file name if exists.
254 (when (eq (nth 1 file-notify--test-event) 'renamed)
255 (should
256 (or (string-equal (file-notify--event-file1-name file-notify--test-event)
257 file-notify--test-tmpfile1)
258 (string-equal (file-notify--event-file1-name file-notify--test-event)
259 temporary-file-directory)))))
260
261 (defun file-notify--test-event-handler (event)
262 "Run a test over FILE-NOTIFY--TEST-EVENT.
263 For later analysis, append the test result to `file-notify--test-results'
264 and the event to `file-notify--test-events'."
265 (let* ((file-notify--test-event event)
266 (result
267 (ert-run-test (make-ert-test :body 'file-notify--test-event-test))))
268 ;; Do not add lock files, this would confuse the checks.
269 (unless (string-match
270 (regexp-quote ".#")
271 (file-notify--event-file-name file-notify--test-event))
272 ;;(message "file-notify--test-event-handler %S" file-notify--test-event)
273 (setq file-notify--test-events
274 (append file-notify--test-events `(,file-notify--test-event))
275 file-notify--test-results
276 (append file-notify--test-results `(,result))))))
277
278 (defun file-notify--test-make-temp-name ()
279 "Create a temporary file name for test."
280 (expand-file-name
281 (make-temp-name "file-notify-test") temporary-file-directory))
282
283 (defmacro file-notify--wait-for-events (timeout until)
284 "Wait for and return file notification events until form UNTIL is true.
285 TIMEOUT is the maximum time to wait for, in seconds."
286 `(with-timeout (,timeout (ignore))
287 (while (null ,until)
288 (read-event nil nil 0.1))))
289
290 (defmacro file-notify--test-with-events (events &rest body)
291 "Run BODY collecting events and then compare with EVENTS.
292 EVENTS is either a simple list of events, or a list of lists of
293 events, which represent different possible results. Don't wait
294 longer than timeout seconds for the events to be delivered."
295 (declare (indent 1))
296 (let ((outer (make-symbol "outer")))
297 `(let* ((,outer file-notify--test-events)
298 (events (if (consp (car ,events)) ,events (list ,events)))
299 (max-length (apply 'max (mapcar 'length events)))
300 create-lockfiles result)
301 ;; Flush pending events.
302 (file-notify--wait-for-events
303 (file-notify--test-timeout)
304 (input-pending-p))
305 (let (file-notify--test-events)
306 ,@body
307 (file-notify--wait-for-events
308 ;; More events need more time. Use some fudge factor.
309 (* (ceiling max-length 100) (file-notify--test-timeout))
310 (= max-length (length file-notify--test-events)))
311 ;; One of the possible results shall match.
312 (should
313 (dolist (elt events result)
314 (setq result
315 (or result
316 (equal elt (mapcar #'cadr file-notify--test-events))))))
317 (setq ,outer (append ,outer file-notify--test-events)))
318 (setq file-notify--test-events ,outer))))
319
320 (ert-deftest file-notify-test02-events ()
321 "Check file creation/change/removal notifications."
322 (skip-unless (file-notify--test-local-enabled))
323
324 (unwind-protect
325 (progn
326 ;; Check file creation, change and deletion. It doesn't work
327 ;; for cygwin and kqueue, because we don't use an implicit
328 ;; directory monitor (kqueue), or the timings are too bad (cygwin).
329 (unless (or (eq system-type 'cygwin)
330 (string-equal (file-notify--test-library) "kqueue"))
331 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
332 (should
333 (setq file-notify--test-desc
334 (file-notify-add-watch
335 file-notify--test-tmpfile
336 '(change) 'file-notify--test-event-handler)))
337 (file-notify--test-with-events
338 (cond
339 ;; cygwin recognizes only `deleted' and `stopped' events.
340 ((eq system-type 'cygwin)
341 '(deleted stopped))
342 (t '(created changed deleted stopped)))
343 (write-region
344 "another text" nil file-notify--test-tmpfile nil 'no-message)
345 (read-event nil nil 0.1)
346 (delete-file file-notify--test-tmpfile))
347 ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it.
348 (let (file-notify--test-events)
349 (file-notify-rm-watch file-notify--test-desc)))
350
351 ;; Check file change and deletion.
352 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
353 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
354 (should
355 (setq file-notify--test-desc
356 (file-notify-add-watch
357 file-notify--test-tmpfile
358 '(change) 'file-notify--test-event-handler)))
359 (file-notify--test-with-events
360 (cond
361 ;; cygwin recognizes only `deleted' and `stopped' events.
362 ((eq system-type 'cygwin)
363 '(deleted stopped))
364 ;; inotify and kqueue raise just one `changed' event.
365 ((or (string-equal "inotify" (file-notify--test-library))
366 (string-equal "kqueue" (file-notify--test-library)))
367 '(changed deleted stopped))
368 ;; gfilenotify raises one or two `changed' events
369 ;; randomly, no chance to test. So we accept both cases.
370 ((string-equal "gfilenotify" (file-notify--test-library))
371 '((changed deleted stopped)
372 (changed changed deleted stopped)))
373 (t '(changed changed deleted stopped)))
374 (read-event nil nil 0.1)
375 (write-region
376 "another text" nil file-notify--test-tmpfile nil 'no-message)
377 (read-event nil nil 0.1)
378 (delete-file file-notify--test-tmpfile))
379 ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it.
380 (let (file-notify--test-events)
381 (file-notify-rm-watch file-notify--test-desc))
382
383 ;; Check file creation, change and deletion when watching a
384 ;; directory. There must be a `stopped' event when deleting
385 ;; the directory.
386 (let ((temporary-file-directory
387 (make-temp-file "file-notify-test-parent" t)))
388 (should
389 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
390 file-notify--test-desc
391 (file-notify-add-watch
392 temporary-file-directory
393 '(change) 'file-notify--test-event-handler)))
394 (file-notify--test-with-events
395 (cond
396 ;; w32notify does raise a `stopped' event when a
397 ;; watched directory is deleted.
398 ((string-equal (file-notify--test-library) "w32notify")
399 '(created changed deleted))
400 ;; cygwin recognizes only `deleted' and `stopped' events.
401 ((eq system-type 'cygwin)
402 '(deleted stopped))
403 ;; There are two `deleted' events, for the file and for
404 ;; the directory. Except for kqueue.
405 ((string-equal (file-notify--test-library) "kqueue")
406 '(created changed deleted stopped))
407 (t '(created changed deleted deleted stopped)))
408 (read-event nil nil 0.1)
409 (write-region
410 "any text" nil file-notify--test-tmpfile nil 'no-message)
411 (read-event nil nil 0.1)
412 (delete-directory temporary-file-directory 'recursive))
413 ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it.
414 (let (file-notify--test-events)
415 (file-notify-rm-watch file-notify--test-desc)))
416
417 ;; Check copy of files inside a directory.
418 (let ((temporary-file-directory
419 (make-temp-file "file-notify-test-parent" t)))
420 (should
421 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
422 file-notify--test-tmpfile1 (file-notify--test-make-temp-name)
423 file-notify--test-desc
424 (file-notify-add-watch
425 temporary-file-directory
426 '(change) 'file-notify--test-event-handler)))
427 (file-notify--test-with-events
428 (cond
429 ;; w32notify does not distinguish between `changed' and
430 ;; `attribute-changed'.
431 ((string-equal (file-notify--test-library) "w32notify")
432 '(created changed created changed changed changed changed
433 deleted deleted))
434 ;; cygwin recognizes only `deleted' and `stopped' events.
435 ((eq system-type 'cygwin)
436 '(deleted stopped))
437 ;; There are three `deleted' events, for two files and
438 ;; for the directory. Except for kqueue.
439 ((string-equal (file-notify--test-library) "kqueue")
440 '(created changed created changed deleted stopped))
441 (t '(created changed created changed
442 deleted deleted deleted stopped)))
443 (read-event nil nil 0.1)
444 (write-region
445 "any text" nil file-notify--test-tmpfile nil 'no-message)
446 (read-event nil nil 0.1)
447 (copy-file file-notify--test-tmpfile file-notify--test-tmpfile1)
448 ;; The next two events shall not be visible.
449 (read-event nil nil 0.1)
450 (set-file-modes file-notify--test-tmpfile 000)
451 (read-event nil nil 0.1)
452 (set-file-times file-notify--test-tmpfile '(0 0))
453 (read-event nil nil 0.1)
454 (delete-directory temporary-file-directory 'recursive))
455 ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it.
456 (let (file-notify--test-events)
457 (file-notify-rm-watch file-notify--test-desc)))
458
459 ;; Check rename of files inside a directory.
460 (let ((temporary-file-directory
461 (make-temp-file "file-notify-test-parent" t)))
462 (should
463 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
464 file-notify--test-tmpfile1 (file-notify--test-make-temp-name)
465 file-notify--test-desc
466 (file-notify-add-watch
467 temporary-file-directory
468 '(change) 'file-notify--test-event-handler)))
469 (file-notify--test-with-events
470 (cond
471 ;; w32notify does not distinguish between `changed' and
472 ;; `attribute-changed'.
473 ((string-equal (file-notify--test-library) "w32notify")
474 '(created changed renamed deleted))
475 ;; cygwin recognizes only `deleted' and `stopped' events.
476 ((eq system-type 'cygwin)
477 '(deleted stopped))
478 ;; There are two `deleted' events, for the file and for
479 ;; the directory. Except for kqueue.
480 ((string-equal (file-notify--test-library) "kqueue")
481 '(created changed renamed deleted stopped))
482 (t '(created changed renamed deleted deleted stopped)))
483 (read-event nil nil 0.1)
484 (write-region
485 "any text" nil file-notify--test-tmpfile nil 'no-message)
486 (read-event nil nil 0.1)
487 (rename-file file-notify--test-tmpfile file-notify--test-tmpfile1)
488 ;; After the rename, we won't get events anymore.
489 (read-event nil nil 0.1)
490 (delete-directory temporary-file-directory 'recursive))
491 ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it.
492 (let (file-notify--test-events)
493 (file-notify-rm-watch file-notify--test-desc)))
494
495 ;; Check attribute change. Does not work for cygwin.
496 (unless (eq system-type 'cygwin)
497 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
498 (write-region
499 "any text" nil file-notify--test-tmpfile nil 'no-message)
500 (should
501 (setq file-notify--test-desc
502 (file-notify-add-watch
503 file-notify--test-tmpfile
504 '(attribute-change) 'file-notify--test-event-handler)))
505 (file-notify--test-with-events
506 (cond
507 ;; w32notify does not distinguish between `changed' and
508 ;; `attribute-changed'.
509 ((string-equal (file-notify--test-library) "w32notify")
510 '(changed changed changed changed))
511 ;; For kqueue and in the remote case, `write-region'
512 ;; raises also an `attribute-changed' event.
513 ((or (string-equal (file-notify--test-library) "kqueue")
514 (file-remote-p temporary-file-directory))
515 '(attribute-changed attribute-changed attribute-changed))
516 (t '(attribute-changed attribute-changed)))
517 (read-event nil nil 0.1)
518 (write-region
519 "any text" nil file-notify--test-tmpfile nil 'no-message)
520 (read-event nil nil 0.1)
521 (set-file-modes file-notify--test-tmpfile 000)
522 (read-event nil nil 0.1)
523 (set-file-times file-notify--test-tmpfile '(0 0))
524 (read-event nil nil 0.1)
525 (delete-file file-notify--test-tmpfile))
526 ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it.
527 (let (file-notify--test-events)
528 (file-notify-rm-watch file-notify--test-desc)))
529
530 ;; Check the global sequence again just to make sure that
531 ;; `file-notify--test-events' has been set correctly.
532 (should file-notify--test-results)
533 (dolist (result file-notify--test-results)
534 (when (ert-test-failed-p result)
535 (ert-fail
536 (cadr (ert-test-result-with-condition-condition result))))))
537
538 ;; Cleanup.
539 (file-notify--test-cleanup)))
540
541 (file-notify--deftest-remote file-notify-test02-events
542 "Check file creation/change/removal notifications for remote files.")
543
544 (require 'autorevert)
545 (setq auto-revert-notify-exclude-dir-regexp "nothing-to-be-excluded"
546 auto-revert-remote-files t
547 auto-revert-stop-on-user-input nil)
548
549 (ert-deftest file-notify-test03-autorevert ()
550 "Check autorevert via file notification."
551 (skip-unless (file-notify--test-local-enabled))
552 ;; `auto-revert-buffers' runs every 5". And we must wait, until the
553 ;; file has been reverted.
554 (let ((timeout (if (file-remote-p temporary-file-directory) 60 10))
555 buf)
556 (unwind-protect
557 (progn
558 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
559
560 (write-region
561 "any text" nil file-notify--test-tmpfile nil 'no-message)
562 (setq buf (find-file-noselect file-notify--test-tmpfile))
563 (with-current-buffer buf
564 (should (string-equal (buffer-string) "any text"))
565 ;; `buffer-stale--default-function' checks for
566 ;; `verify-visited-file-modtime'. We must ensure that it
567 ;; returns nil.
568 (sleep-for 1)
569 (auto-revert-mode 1)
570
571 ;; `auto-revert-buffers' runs every 5".
572 (with-timeout (timeout (ignore))
573 (while (null auto-revert-notify-watch-descriptor)
574 (sleep-for 1)))
575
576 ;; Check, that file notification has been used.
577 (should auto-revert-mode)
578 (should auto-revert-use-notify)
579 (should auto-revert-notify-watch-descriptor)
580
581 ;; Modify file. We wait for a second, in order to have
582 ;; another timestamp.
583 (with-current-buffer (get-buffer-create "*Messages*")
584 (narrow-to-region (point-max) (point-max)))
585 (sleep-for 1)
586 (write-region
587 "another text" nil file-notify--test-tmpfile nil 'no-message)
588
589 ;; Check, that the buffer has been reverted.
590 (with-current-buffer (get-buffer-create "*Messages*")
591 (file-notify--wait-for-events
592 timeout
593 (string-match
594 (format-message "Reverting buffer `%s'." (buffer-name buf))
595 (buffer-string))))
596 (should (string-match "another text" (buffer-string)))
597
598 ;; Stop file notification. Autorevert shall still work via polling.
599 ;; It doesn't work for w32notify.
600 (unless (string-equal (file-notify--test-library) "w32notify")
601 (file-notify-rm-watch auto-revert-notify-watch-descriptor)
602 (file-notify--wait-for-events
603 timeout (null auto-revert-use-notify))
604 (should-not auto-revert-use-notify)
605 (should-not auto-revert-notify-watch-descriptor)
606
607 ;; Modify file. We wait for two seconds, in order to
608 ;; have another timestamp. One second seems to be too
609 ;; short.
610 (with-current-buffer (get-buffer-create "*Messages*")
611 (narrow-to-region (point-max) (point-max)))
612 (sleep-for 2)
613 (write-region
614 "foo bla" nil file-notify--test-tmpfile nil 'no-message)
615
616 ;; Check, that the buffer has been reverted.
617 (with-current-buffer (get-buffer-create "*Messages*")
618 (file-notify--wait-for-events
619 timeout
620 (string-match
621 (format-message "Reverting buffer `%s'." (buffer-name buf))
622 (buffer-string))))
623 (should (string-match "foo bla" (buffer-string))))))
624
625 ;; Cleanup.
626 (with-current-buffer "*Messages*" (widen))
627 (ignore-errors (kill-buffer buf))
628 (file-notify--test-cleanup))))
629
630 (file-notify--deftest-remote file-notify-test03-autorevert
631 "Check autorevert via file notification for remote files.")
632
633 (ert-deftest file-notify-test04-file-validity ()
634 "Check `file-notify-valid-p' for files."
635 (skip-unless (file-notify--test-local-enabled))
636
637 (unwind-protect
638 (progn
639 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
640 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
641 (should
642 (setq file-notify--test-desc
643 (file-notify-add-watch
644 file-notify--test-tmpfile
645 '(change) #'file-notify--test-event-handler)))
646 (should (file-notify-valid-p file-notify--test-desc))
647 ;; After calling `file-notify-rm-watch', the descriptor is not
648 ;; valid anymore.
649 (file-notify-rm-watch file-notify--test-desc)
650 (should-not (file-notify-valid-p file-notify--test-desc))
651 (delete-file file-notify--test-tmpfile))
652
653 ;; Cleanup.
654 (file-notify--test-cleanup))
655
656 (unwind-protect
657 (progn
658 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
659 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
660 (should
661 (setq file-notify--test-desc
662 (file-notify-add-watch
663 file-notify--test-tmpfile
664 '(change) #'file-notify--test-event-handler)))
665 (file-notify--test-with-events
666 (cond
667 ;; cygwin recognizes only `deleted' and `stopped' events.
668 ((eq system-type 'cygwin)
669 '(deleted stopped))
670 ;; inotify and kqueue raise just one `changed' event.
671 ((or (string-equal "inotify" (file-notify--test-library))
672 (string-equal "kqueue" (file-notify--test-library)))
673 '(changed deleted stopped))
674 ;; gfilenotify raises one or two `changed' events
675 ;; randomly, no chance to test. So we accept both cases.
676 ((string-equal "gfilenotify" (file-notify--test-library))
677 '((changed deleted stopped)
678 (changed changed deleted stopped)))
679 (t '(changed changed deleted stopped)))
680 (should (file-notify-valid-p file-notify--test-desc))
681 (read-event nil nil 0.1)
682 (write-region
683 "another text" nil file-notify--test-tmpfile nil 'no-message)
684 (read-event nil nil 0.1)
685 (delete-file file-notify--test-tmpfile))
686 ;; After deleting the file, the descriptor is not valid anymore.
687 (should-not (file-notify-valid-p file-notify--test-desc))
688 (file-notify-rm-watch file-notify--test-desc))
689
690 ;; Cleanup.
691 (file-notify--test-cleanup))
692
693 (unwind-protect
694 ;; w32notify does not send a `stopped' event when deleting a
695 ;; directory. The test does not work, therefore.
696 (unless (string-equal (file-notify--test-library) "w32notify")
697 (let ((temporary-file-directory
698 (make-temp-file "file-notify-test-parent" t)))
699 (should
700 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
701 file-notify--test-desc
702 (file-notify-add-watch
703 temporary-file-directory
704 '(change) #'file-notify--test-event-handler)))
705 (file-notify--test-with-events
706 (cond
707 ;; cygwin recognizes only `deleted' and `stopped' events.
708 ((eq system-type 'cygwin)
709 '(deleted stopped))
710 ;; There are two `deleted' events, for the file and for
711 ;; the directory. Except for kqueue.
712 ((string-equal (file-notify--test-library) "kqueue")
713 '(created changed deleted stopped))
714 (t '(created changed deleted deleted stopped)))
715 (should (file-notify-valid-p file-notify--test-desc))
716 (read-event nil nil 0.1)
717 (write-region
718 "any text" nil file-notify--test-tmpfile nil 'no-message)
719 (read-event nil nil 0.1)
720 (delete-directory temporary-file-directory t))
721 ;; After deleting the parent directory, the descriptor must
722 ;; not be valid anymore.
723 (should-not (file-notify-valid-p file-notify--test-desc))))
724
725 ;; Cleanup.
726 (file-notify--test-cleanup)))
727
728 (file-notify--deftest-remote file-notify-test04-file-validity
729 "Check `file-notify-valid-p' via file notification for remote files.")
730
731 (ert-deftest file-notify-test05-dir-validity ()
732 "Check `file-notify-valid-p' for directories."
733 (skip-unless (file-notify--test-local-enabled))
734
735 (unwind-protect
736 (progn
737 (setq file-notify--test-tmpfile
738 (file-name-as-directory (file-notify--test-make-temp-name)))
739 (make-directory file-notify--test-tmpfile)
740 (should
741 (setq file-notify--test-desc
742 (file-notify-add-watch
743 file-notify--test-tmpfile
744 '(change) #'file-notify--test-event-handler)))
745 (should (file-notify-valid-p file-notify--test-desc))
746 ;; After removing the watch, the descriptor must not be valid
747 ;; anymore.
748 (file-notify-rm-watch file-notify--test-desc)
749 (file-notify--wait-for-events
750 (file-notify--test-timeout)
751 (not (file-notify-valid-p file-notify--test-desc)))
752 (should-not (file-notify-valid-p file-notify--test-desc)))
753
754 ;; Cleanup.
755 (file-notify--test-cleanup))
756
757 (unwind-protect
758 ;; The batch-mode operation of w32notify is fragile (there's no
759 ;; input threads to send the message to).
760 (unless (and noninteractive
761 (string-equal (file-notify--test-library) "w32notify"))
762 (setq file-notify--test-tmpfile
763 (file-name-as-directory (file-notify--test-make-temp-name)))
764 (make-directory file-notify--test-tmpfile)
765 (should
766 (setq file-notify--test-desc
767 (file-notify-add-watch
768 file-notify--test-tmpfile
769 '(change) #'file-notify--test-event-handler)))
770 (should (file-notify-valid-p file-notify--test-desc))
771 ;; After deleting the directory, the descriptor must not be
772 ;; valid anymore.
773 (delete-directory file-notify--test-tmpfile t)
774 (file-notify--wait-for-events
775 (file-notify--test-timeout)
776 (not (file-notify-valid-p file-notify--test-desc)))
777 (should-not (file-notify-valid-p file-notify--test-desc)))
778
779 ;; Cleanup.
780 (file-notify--test-cleanup)))
781
782 (file-notify--deftest-remote file-notify-test05-dir-validity
783 "Check `file-notify-valid-p' via file notification for remote directories.")
784
785 (ert-deftest file-notify-test06-many-events ()
786 "Check that events are not dropped."
787 :tags '(:expensive-test)
788 (skip-unless (file-notify--test-local-enabled))
789 ;; Under cygwin events arrive in random order. Impossible to define a test.
790 (skip-unless (not (eq system-type 'cygwin)))
791
792 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
793 (make-directory file-notify--test-tmpfile)
794 (should
795 (setq file-notify--test-desc
796 (file-notify-add-watch
797 file-notify--test-tmpfile
798 '(change) 'file-notify--test-event-handler)))
799 (unwind-protect
800 (let ((n 1000)
801 source-file-list target-file-list
802 (default-directory file-notify--test-tmpfile))
803 (dotimes (i n)
804 ;; It matters which direction we rename, at least for
805 ;; kqueue. This backend parses directories in alphabetic
806 ;; order (x%d before y%d). So we rename both directions.
807 (if (zerop (mod i 2))
808 (progn
809 (push (expand-file-name (format "x%d" i)) source-file-list)
810 (push (expand-file-name (format "y%d" i)) target-file-list))
811 (push (expand-file-name (format "y%d" i)) source-file-list)
812 (push (expand-file-name (format "x%d" i)) target-file-list)))
813 (file-notify--test-with-events (make-list (+ n n) 'created)
814 (let ((source-file-list source-file-list)
815 (target-file-list target-file-list))
816 (while (and source-file-list target-file-list)
817 (read-event nil nil 0.1)
818 (write-region "" nil (pop source-file-list) nil 'no-message)
819 (read-event nil nil 0.1)
820 (write-region "" nil (pop target-file-list) nil 'no-message))))
821 (file-notify--test-with-events
822 (cond
823 ;; w32notify fires both `deleted' and `renamed' events.
824 ((string-equal (file-notify--test-library) "w32notify")
825 (let (r)
826 (dotimes (_i n r)
827 (setq r (append '(deleted renamed) r)))))
828 (t (make-list n 'renamed)))
829 (let ((source-file-list source-file-list)
830 (target-file-list target-file-list))
831 (while (and source-file-list target-file-list)
832 (rename-file (pop source-file-list) (pop target-file-list) t)
833 (read-event nil nil 0.02))))
834 (file-notify--test-with-events (make-list n 'deleted)
835 (dolist (file target-file-list)
836 (prog1 (delete-file file) (read-event nil nil 0.02)))))
837 (file-notify--test-cleanup)))
838
839 (file-notify--deftest-remote file-notify-test06-many-events
840 "Check that events are not dropped for remote directories.")
841
842 (defun file-notify-test-all (&optional interactive)
843 "Run all tests for \\[file-notify]."
844 (interactive "p")
845 (if interactive
846 (ert-run-tests-interactively "^file-notify-")
847 (ert-run-tests-batch "^file-notify-")))
848
849 ;; TODO:
850
851 ;; * For w32notify, no stopped events arrive when a directory is removed.
852 ;; * Check, why cygwin recognizes only `deleted' and `stopped' events.
853
854 (provide 'file-notify-tests)
855 ;;; file-notify-tests.el ends here