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