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