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