Give '$' punctuation syntax in make-mode (Bug#24477)
[emacs.git] / test / lisp / filenotify-tests.el
blob56403f430924c937b525287245fa38b6272b4397
1 ;;; filenotify-tests.el --- Tests of file notifications -*- lexical-binding: t; -*-
3 ;; Copyright (C) 2013-2018 Free Software Foundation, Inc.
5 ;; Author: Michael Albinus <michael.albinus@gmx.de>
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.
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.
17 ;; You should have received a copy of the GNU General Public License
18 ;; along with this program. If not, see `https://www.gnu.org/licenses/'.
20 ;;; Commentary:
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.
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.
34 ;; A whole test run can be performed calling the command `file-notify-test-all'.
36 ;;; Code:
38 (require 'ert)
39 (require 'ert-x)
40 (require 'filenotify)
41 (require 'tramp)
43 ;; There is no default value on w32 systems, which could work out of the box.
44 (defconst file-notify-test-remote-temporary-file-directory
45 (cond
46 ((getenv "REMOTE_TEMPORARY_FILE_DIRECTORY"))
47 ((eq system-type 'windows-nt) null-device)
48 (t (add-to-list
49 'tramp-methods
50 '("mock"
51 (tramp-login-program "sh")
52 (tramp-login-args (("-i")))
53 (tramp-remote-shell "/bin/sh")
54 (tramp-remote-shell-args ("-c"))
55 (tramp-connection-timeout 10)))
56 (add-to-list
57 'tramp-default-host-alist
58 `("\\`mock\\'" nil ,(system-name)))
59 ;; Emacs' Makefile sets $HOME to a nonexistent value. Needed in
60 ;; batch mode only, therefore. `temporary-file-directory' might
61 ;; be quoted, so we unquote it just in case.
62 (unless (and (null noninteractive) (file-directory-p "~/"))
63 (setenv "HOME" (file-name-unquote temporary-file-directory)))
64 (format "/mock::%s" temporary-file-directory)))
65 "Temporary directory for Tramp tests.")
67 (defvar file-notify--test-tmpdir nil)
68 (defvar file-notify--test-tmpfile nil)
69 (defvar file-notify--test-tmpfile1 nil)
70 (defvar file-notify--test-desc nil)
71 (defvar file-notify--test-desc1 nil)
72 (defvar file-notify--test-desc2 nil)
73 (defvar file-notify--test-results nil)
74 (defvar file-notify--test-event nil)
75 (defvar file-notify--test-events nil)
76 (defvar file-notify--test-monitors nil)
78 (defun file-notify--test-read-event ()
79 "Read one event.
80 There are different timeouts for local and remote file notification libraries."
81 (read-event
82 nil nil
83 (cond
84 ;; gio/gpollfilemonitor.c declares POLL_TIME_SECS 5. So we must
85 ;; wait at least this time in the GPollFileMonitor case. A
86 ;; similar timeout seems to be needed in the GFamFileMonitor case,
87 ;; at least on Cygwin.
88 ((and (string-equal (file-notify--test-library) "gfilenotify")
89 (memq (file-notify--test-monitor)
90 '(GFamFileMonitor GPollFileMonitor)))
92 ((string-equal (file-notify--test-library) "gvfs-monitor-dir.exe") 1)
93 ((file-remote-p temporary-file-directory) 0.1)
94 (t 0.01))))
96 (defun file-notify--test-timeout ()
97 "Timeout to wait for arriving a bunch of events, in seconds."
98 (cond
99 ((file-remote-p temporary-file-directory) 6)
100 ((string-equal (file-notify--test-library) "w32notify") 4)
101 ((eq system-type 'cygwin) 6)
102 (t 3)))
104 (defmacro file-notify--wait-for-events (timeout until)
105 "Wait for and return file notification events until form UNTIL is true.
106 TIMEOUT is the maximum time to wait for, in seconds."
107 `(with-timeout (,timeout (ignore))
108 (while (null ,until)
109 (file-notify--test-read-event))))
111 (defun file-notify--test-no-descriptors ()
112 "Check that `file-notify-descriptors' is an empty hash table.
113 Return nil when any other file notification watch is still active."
114 ;; Give read events a last chance.
115 (file-notify--wait-for-events
116 (file-notify--test-timeout)
117 (zerop (hash-table-count file-notify-descriptors)))
118 ;; Now check.
119 (zerop (hash-table-count file-notify-descriptors)))
121 (defun file-notify--test-no-descriptors-explainer ()
122 "Explain why `file-notify--test-no-descriptors' fails."
123 (let ((result (list "Watch descriptor(s) existent:")))
124 (maphash
125 (lambda (key value) (push (cons key value) result))
126 file-notify-descriptors)
127 (nreverse result)))
129 (put 'file-notify--test-no-descriptors 'ert-explainer
130 'file-notify--test-no-descriptors-explainer)
132 (defun file-notify--test-cleanup-p ()
133 "Check, that the test has cleaned up the environment as much as needed."
134 ;; `file-notify--test-event' should not be set but bound
135 ;; dynamically.
136 (should-not file-notify--test-event)
137 ;; The test should have cleaned up this already. Let's check
138 ;; nevertheless.
139 (should (file-notify--test-no-descriptors)))
141 (defun file-notify--test-cleanup ()
142 "Cleanup after a test."
143 (file-notify-rm-watch file-notify--test-desc)
144 (file-notify-rm-watch file-notify--test-desc1)
145 (file-notify-rm-watch file-notify--test-desc2)
147 (ignore-errors
148 (delete-file (file-newest-backup file-notify--test-tmpfile)))
149 (ignore-errors
150 (if (file-directory-p file-notify--test-tmpfile)
151 (delete-directory file-notify--test-tmpfile 'recursive)
152 (delete-file file-notify--test-tmpfile)))
153 (ignore-errors
154 (if (file-directory-p file-notify--test-tmpfile1)
155 (delete-directory file-notify--test-tmpfile1 'recursive)
156 (delete-file file-notify--test-tmpfile1)))
157 (ignore-errors
158 (delete-directory file-notify--test-tmpdir 'recursive))
159 (ignore-errors
160 (when (file-remote-p temporary-file-directory)
161 (tramp-cleanup-connection
162 (tramp-dissect-file-name temporary-file-directory) nil 'keep-password)))
164 (when (hash-table-p file-notify-descriptors)
165 (clrhash file-notify-descriptors))
167 (setq file-notify--test-tmpdir nil
168 file-notify--test-tmpfile nil
169 file-notify--test-tmpfile1 nil
170 file-notify--test-desc nil
171 file-notify--test-desc1 nil
172 file-notify--test-desc2 nil
173 file-notify--test-results nil
174 file-notify--test-events nil
175 file-notify--test-monitors nil))
177 (setq password-cache-expiry nil
178 tramp-verbose 0
179 tramp-message-show-message nil)
181 ;; This should happen on hydra only.
182 (when (getenv "EMACS_HYDRA_CI")
183 (add-to-list 'tramp-remote-path 'tramp-own-remote-path))
185 ;; We do not want to try and fail `file-notify-add-watch'.
186 (defun file-notify--test-local-enabled ()
187 "Whether local file notification is enabled.
188 This is needed for local `temporary-file-directory' only, in the
189 remote case we return always t."
190 (or file-notify--library
191 (file-remote-p temporary-file-directory)))
193 (defvar file-notify--test-remote-enabled-checked nil
194 "Cached result of `file-notify--test-remote-enabled'.
195 If the function did run, the value is a cons cell, the `cdr'
196 being the result.")
198 (defun file-notify--test-remote-enabled ()
199 "Whether remote file notification is enabled."
200 (unless (consp file-notify--test-remote-enabled-checked)
201 (let (desc)
202 (ignore-errors
203 (and
204 (file-remote-p file-notify-test-remote-temporary-file-directory)
205 (file-directory-p file-notify-test-remote-temporary-file-directory)
206 (file-writable-p file-notify-test-remote-temporary-file-directory)
207 (setq desc
208 (file-notify-add-watch
209 file-notify-test-remote-temporary-file-directory
210 '(change) #'ignore))))
211 (setq file-notify--test-remote-enabled-checked (cons t desc))
212 (when desc (file-notify-rm-watch desc))))
213 ;; Return result.
214 (cdr file-notify--test-remote-enabled-checked))
216 (defun file-notify--test-library ()
217 "The used library for the test, as a string.
218 In the remote case, it is the process name which runs on the
219 remote host, or nil."
220 (if (null (file-remote-p temporary-file-directory))
221 (symbol-name file-notify--library)
222 (and (consp file-notify--test-remote-enabled-checked)
223 (processp (cdr file-notify--test-remote-enabled-checked))
224 (replace-regexp-in-string
225 "<[[:digit:]]+>\\'" ""
226 (process-name (cdr file-notify--test-remote-enabled-checked))))))
228 (defun file-notify--test-monitor ()
229 "The used monitor for the test, as a symbol.
230 This returns only for the local case and gfilenotify; otherwise it is nil.
231 `file-notify--test-desc' must be a valid watch descriptor."
232 ;; We cache the result, because after `file-notify-rm-watch',
233 ;; `gfile-monitor-name' does not return a proper result anymore.
234 ;; But we still need this information.
235 (unless (file-remote-p temporary-file-directory)
236 (or (cdr (assq file-notify--test-desc file-notify--test-monitors))
237 (when (functionp 'gfile-monitor-name)
238 (add-to-list 'file-notify--test-monitors
239 (cons file-notify--test-desc
240 (gfile-monitor-name file-notify--test-desc)))
241 (cdr (assq file-notify--test-desc file-notify--test-monitors))))))
243 (defmacro file-notify--deftest-remote (test docstring)
244 "Define ert `TEST-remote' for remote files."
245 (declare (indent 1))
246 `(ert-deftest ,(intern (concat (symbol-name test) "-remote")) ()
247 ,docstring
248 :tags '(:expensive-test)
249 (let* ((temporary-file-directory
250 file-notify-test-remote-temporary-file-directory)
251 (ert-test (ert-get-test ',test)))
252 (skip-unless (file-notify--test-remote-enabled))
253 (tramp-cleanup-connection
254 (tramp-dissect-file-name temporary-file-directory) nil 'keep-password)
255 (funcall (ert-test-body ert-test)))))
257 (ert-deftest file-notify-test00-availability ()
258 "Test availability of `file-notify'."
259 (skip-unless (file-notify--test-local-enabled))
261 (unwind-protect
262 (progn
263 ;; Report the native library which has been used.
264 (message "Library: `%s'" (file-notify--test-library))
265 (should
266 (setq file-notify--test-desc
267 (file-notify-add-watch
268 temporary-file-directory '(change) #'ignore)))
269 (when (file-notify--test-monitor)
270 (message "Monitor: `%s'" (file-notify--test-monitor)))
271 (file-notify-rm-watch file-notify--test-desc)
273 ;; The environment shall be cleaned up.
274 (file-notify--test-cleanup-p))
276 ;; Cleanup.
277 (file-notify--test-cleanup)))
279 (file-notify--deftest-remote file-notify-test00-availability
280 "Test availability of `file-notify' for remote files.")
282 (defun file-notify--test-make-temp-name ()
283 "Create a temporary file name for test."
284 (unless (stringp file-notify--test-tmpdir)
285 (setq file-notify--test-tmpdir
286 (expand-file-name
287 (make-temp-name "file-notify-test") temporary-file-directory)))
288 (unless (file-directory-p file-notify--test-tmpdir)
289 (make-directory file-notify--test-tmpdir))
290 (expand-file-name
291 (make-temp-name "file-notify-test") file-notify--test-tmpdir))
293 (ert-deftest file-notify-test01-add-watch ()
294 "Check `file-notify-add-watch'."
295 (skip-unless (file-notify--test-local-enabled))
297 (unwind-protect
298 (progn
299 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
300 file-notify--test-tmpfile1
301 (format
302 "%s/%s" file-notify--test-tmpfile (md5 (current-time-string))))
304 ;; Check, that different valid parameters are accepted.
305 (should
306 (setq file-notify--test-desc
307 (file-notify-add-watch
308 file-notify--test-tmpdir '(change) #'ignore)))
309 (file-notify-rm-watch file-notify--test-desc)
310 (should
311 (setq file-notify--test-desc
312 (file-notify-add-watch
313 file-notify--test-tmpdir '(attribute-change) #'ignore)))
314 (file-notify-rm-watch file-notify--test-desc)
315 (should
316 (setq file-notify--test-desc
317 (file-notify-add-watch
318 file-notify--test-tmpdir '(change attribute-change) #'ignore)))
319 (file-notify-rm-watch file-notify--test-desc)
321 ;; File monitors like kqueue insist, that the watched file
322 ;; exists. Directory monitors are not bound to this
323 ;; restriction.
324 (when (string-equal (file-notify--test-library) "kqueue")
325 (write-region
326 "any text" nil file-notify--test-tmpfile nil 'no-message))
327 (should
328 (setq file-notify--test-desc
329 (file-notify-add-watch
330 file-notify--test-tmpfile '(change attribute-change) #'ignore)))
331 (file-notify-rm-watch file-notify--test-desc)
332 (when (string-equal (file-notify--test-library) "kqueue")
333 (delete-file file-notify--test-tmpfile))
335 ;; Check error handling.
336 (should-error (file-notify-add-watch 1 2 3 4)
337 :type 'wrong-number-of-arguments)
338 (should
339 (equal (should-error
340 (file-notify-add-watch 1 2 3))
341 '(wrong-type-argument 1)))
342 (should
343 (equal (should-error
344 (file-notify-add-watch file-notify--test-tmpdir 2 3))
345 '(wrong-type-argument 2)))
346 (should
347 (equal (should-error
348 (file-notify-add-watch file-notify--test-tmpdir '(change) 3))
349 '(wrong-type-argument 3)))
350 ;; The upper directory of a file must exist.
351 (should
352 (equal (should-error
353 (file-notify-add-watch
354 file-notify--test-tmpfile1
355 '(change attribute-change) #'ignore))
356 `(file-notify-error
357 "Directory does not exist" ,file-notify--test-tmpfile)))
359 ;; The environment shall be cleaned up.
360 (file-notify--test-cleanup-p))
362 ;; Cleanup.
363 (file-notify--test-cleanup)))
365 (file-notify--deftest-remote file-notify-test01-add-watch
366 "Check `file-notify-add-watch' for remote files.")
368 ;; This test is inspired by Bug#26126 and Bug#26127.
369 (ert-deftest file-notify-test02-rm-watch ()
370 "Check `file-notify-rm-watch'."
371 (skip-unless (file-notify--test-local-enabled))
373 (unwind-protect
374 ;; Check, that `file-notify-rm-watch' works.
375 (progn
376 (should
377 (setq file-notify--test-desc
378 (file-notify-add-watch
379 temporary-file-directory '(change) #'ignore)))
380 (file-notify-rm-watch file-notify--test-desc)
381 ;; Check, that any parameter is accepted.
382 (condition-case err
383 (progn
384 (file-notify-rm-watch nil)
385 (file-notify-rm-watch 0)
386 (file-notify-rm-watch "foo")
387 (file-notify-rm-watch 'foo))
388 (error (ert-fail err)))
390 ;; The environment shall be cleaned up.
391 (file-notify--test-cleanup-p))
393 ;; Cleanup.
394 (file-notify--test-cleanup))
396 (unwind-protect
397 ;; Check, that no error is returned removing a watch descriptor twice.
398 (progn
399 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
400 file-notify--test-tmpfile1 (file-notify--test-make-temp-name))
401 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
402 (write-region "any text" nil file-notify--test-tmpfile1 nil 'no-message)
403 (should
404 (setq file-notify--test-desc
405 (file-notify-add-watch
406 file-notify--test-tmpfile '(change) #'ignore)))
407 (should
408 (setq file-notify--test-desc1
409 (file-notify-add-watch
410 file-notify--test-tmpfile1 '(change) #'ignore)))
411 ;; Remove `file-notify--test-desc' twice.
412 (file-notify-rm-watch file-notify--test-desc)
413 (file-notify-rm-watch file-notify--test-desc)
414 (file-notify-rm-watch file-notify--test-desc1)
415 (delete-file file-notify--test-tmpfile)
416 (delete-file file-notify--test-tmpfile1)
418 ;; The environment shall be cleaned up.
419 (file-notify--test-cleanup-p))
421 ;; Cleanup.
422 (file-notify--test-cleanup))
424 (unwind-protect
425 ;; Check, that removing watch descriptors out of order do not
426 ;; harm. This fails on Cygwin because of timing issues unless a
427 ;; long `sit-for' is added before the call to
428 ;; `file-notify--test-read-event'.
429 (if (not (eq system-type 'cygwin))
430 (let (results)
431 (cl-flet ((first-callback (event)
432 (when (eq (nth 1 event) 'deleted) (push 1 results)))
433 (second-callback (event)
434 (when (eq (nth 1 event) 'deleted) (push 2 results))))
435 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
436 (write-region
437 "any text" nil file-notify--test-tmpfile nil 'no-message)
438 (should
439 (setq file-notify--test-desc
440 (file-notify-add-watch
441 file-notify--test-tmpfile
442 '(change) #'first-callback)))
443 (should
444 (setq file-notify--test-desc1
445 (file-notify-add-watch
446 file-notify--test-tmpfile
447 '(change) #'second-callback)))
448 ;; Remove first watch.
449 (file-notify-rm-watch file-notify--test-desc)
450 ;; Only the second callback shall run.
451 (file-notify--test-read-event)
452 (delete-file file-notify--test-tmpfile)
453 (file-notify--wait-for-events
454 (file-notify--test-timeout) results)
455 (should (equal results (list 2)))
457 ;; The environment shall be cleaned up.
458 (file-notify--test-cleanup-p))))
460 ;; Cleanup.
461 (file-notify--test-cleanup)))
463 (file-notify--deftest-remote file-notify-test02-rm-watch
464 "Check `file-notify-rm-watch' for remote files.")
466 (defun file-notify--test-event-test ()
467 "Ert test function to be called by `file-notify--test-event-handler'.
468 We cannot pass arguments, so we assume that `file-notify--test-event'
469 is bound somewhere."
470 ;; Check the descriptor.
471 (should (equal (car file-notify--test-event) file-notify--test-desc))
472 ;; Check the file name.
473 (should
474 (string-prefix-p
475 (file-notify--event-watched-file file-notify--test-event)
476 (file-notify--event-file-name file-notify--test-event)))
477 ;; Check the second file name if exists.
478 (when (eq (nth 1 file-notify--test-event) 'renamed)
479 (should
480 (string-prefix-p
481 (file-notify--event-watched-file file-notify--test-event)
482 (file-notify--event-file1-name file-notify--test-event)))))
484 (defun file-notify--test-event-handler (event)
485 "Run a test over FILE-NOTIFY--TEST-EVENT.
486 For later analysis, append the test result to `file-notify--test-results'
487 and the event to `file-notify--test-events'."
488 (let* ((file-notify--test-event event)
489 (result
490 (ert-run-test (make-ert-test :body 'file-notify--test-event-test))))
491 ;; Do not add lock files, this would confuse the checks.
492 (unless (string-match
493 (regexp-quote ".#")
494 (file-notify--event-file-name file-notify--test-event))
495 ;;(message "file-notify--test-event-handler result: %s event: %S"
496 ;;(null (ert-test-failed-p result)) file-notify--test-event)
497 (setq file-notify--test-events
498 (append file-notify--test-events `(,file-notify--test-event))
499 file-notify--test-results
500 (append file-notify--test-results `(,result))))))
502 (defun file-notify--test-with-events-check (events)
503 "Check whether received events match one of the EVENTS alternatives."
504 (let (result)
505 (dolist (elt events result)
506 (setq result
507 (or result
508 (if (eq (car elt) :random)
509 (equal (sort (cdr elt) 'string-lessp)
510 (sort (mapcar #'cadr file-notify--test-events)
511 'string-lessp))
512 (equal elt (mapcar #'cadr file-notify--test-events))))))))
514 (defun file-notify--test-with-events-explainer (events)
515 "Explain why `file-notify--test-with-events-check' fails."
516 (if (null (cdr events))
517 (format "Received events do not match expected events\n%s\n%s"
518 (mapcar #'cadr file-notify--test-events) (car events))
519 (format
520 "Received events do not match any sequence of expected events\n%s\n%s"
521 (mapcar #'cadr file-notify--test-events) events)))
523 (put 'file-notify--test-with-events-check 'ert-explainer
524 'file-notify--test-with-events-explainer)
526 (defmacro file-notify--test-with-events (events &rest body)
527 "Run BODY collecting events and then compare with EVENTS.
528 EVENTS is either a simple list of events, or a list of lists of
529 events, which represent different possible results. The first
530 event of a list could be the pseudo event `:random', which is
531 just an indicator for comparison.
533 Don't wait longer than timeout seconds for the events to be
534 delivered."
535 (declare (indent 1))
536 `(let* ((events (if (consp (car ,events)) ,events (list ,events)))
537 (max-length
538 (apply
539 'max
540 (mapcar
541 (lambda (x) (length (if (eq (car x) :random) (cdr x) x)))
542 events)))
543 create-lockfiles)
544 ;; Flush pending events.
545 (file-notify--test-read-event)
546 (file-notify--wait-for-events
547 (file-notify--test-timeout)
548 (not (input-pending-p)))
549 (setq file-notify--test-events nil
550 file-notify--test-results nil)
551 ,@body
552 (file-notify--wait-for-events
553 ;; More events need more time. Use some fudge factor.
554 (* (ceiling max-length 100) (file-notify--test-timeout))
555 (= max-length (length file-notify--test-events)))
556 ;; Check the result sequence just to make sure that all events
557 ;; are as expected.
558 (dolist (result file-notify--test-results)
559 (when (ert-test-failed-p result)
560 (ert-fail
561 (cadr (ert-test-result-with-condition-condition result)))))
562 ;; One of the possible event sequences shall match.
563 (should (file-notify--test-with-events-check events))))
565 (ert-deftest file-notify-test03-events ()
566 "Check file creation/change/removal notifications."
567 (skip-unless (file-notify--test-local-enabled))
569 (unwind-protect
570 ;; Check file creation, change and deletion. It doesn't work
571 ;; for kqueue, because we don't use an implicit directory
572 ;; monitor.
573 (unless (string-equal (file-notify--test-library) "kqueue")
574 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
575 (should
576 (setq file-notify--test-desc
577 (file-notify-add-watch
578 file-notify--test-tmpfile
579 '(change) #'file-notify--test-event-handler)))
580 (file-notify--test-with-events
581 (cond
582 ;; gvfs-monitor-dir on cygwin does not detect the
583 ;; `created' event reliably.
584 ((string-equal
585 (file-notify--test-library) "gvfs-monitor-dir.exe")
586 '((deleted stopped)
587 (created deleted stopped)))
588 ;; cygwin does not raise a `changed' event.
589 ((eq system-type 'cygwin)
590 '(created deleted stopped))
591 (t '(created changed deleted stopped)))
592 (write-region
593 "another text" nil file-notify--test-tmpfile nil 'no-message)
594 (file-notify--test-read-event)
595 (delete-file file-notify--test-tmpfile))
596 (file-notify-rm-watch file-notify--test-desc)
598 ;; The environment shall be cleaned up.
599 (file-notify--test-cleanup-p))
601 ;; Cleanup.
602 (file-notify--test-cleanup))
604 (unwind-protect
605 (progn
606 ;; Check file change and deletion.
607 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
608 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
609 (should
610 (setq file-notify--test-desc
611 (file-notify-add-watch
612 file-notify--test-tmpfile
613 '(change) #'file-notify--test-event-handler)))
614 (file-notify--test-with-events
615 (cond
616 ;; gvfs-monitor-dir on cygwin does not detect the
617 ;; `changed' event reliably.
618 ((string-equal (file-notify--test-library) "gvfs-monitor-dir.exe")
619 '((deleted stopped)
620 (changed deleted stopped)))
621 ;; There could be one or two `changed' events.
622 (t '((changed deleted stopped)
623 (changed changed deleted stopped))))
624 (write-region
625 "another text" nil file-notify--test-tmpfile nil 'no-message)
626 (file-notify--test-read-event)
627 (delete-file file-notify--test-tmpfile))
628 (file-notify-rm-watch file-notify--test-desc)
630 ;; The environment shall be cleaned up.
631 (file-notify--test-cleanup-p))
633 ;; Cleanup.
634 (file-notify--test-cleanup))
636 (unwind-protect
637 ;; Check file creation, change and deletion when watching a
638 ;; directory. There must be a `stopped' event when deleting the
639 ;; directory.
640 (let ((file-notify--test-tmpdir
641 (make-temp-file "file-notify-test-parent" t)))
642 (should
643 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
644 file-notify--test-desc
645 (file-notify-add-watch
646 file-notify--test-tmpdir
647 '(change) #'file-notify--test-event-handler)))
648 (file-notify--test-with-events
649 (cond
650 ;; w32notify does not raise `deleted' and `stopped'
651 ;; events for the watched directory.
652 ((string-equal (file-notify--test-library) "w32notify")
653 '(created changed deleted))
654 ;; gvfs-monitor-dir on cygwin does not detect the
655 ;; `created' event reliably.
656 ((string-equal
657 (file-notify--test-library) "gvfs-monitor-dir.exe")
658 '((deleted stopped)
659 (created deleted stopped)))
660 ;; There are two `deleted' events, for the file and for
661 ;; the directory. Except for cygwin and kqueue. And
662 ;; cygwin does not raise a `changed' event.
663 ((eq system-type 'cygwin)
664 '(created deleted stopped))
665 ((string-equal (file-notify--test-library) "kqueue")
666 '(created changed deleted stopped))
667 (t '(created changed deleted deleted stopped)))
668 (write-region
669 "any text" nil file-notify--test-tmpfile nil 'no-message)
670 (file-notify--test-read-event)
671 (delete-directory file-notify--test-tmpdir 'recursive))
672 (file-notify-rm-watch file-notify--test-desc)
674 ;; The environment shall be cleaned up.
675 (file-notify--test-cleanup-p))
677 ;; Cleanup.
678 (file-notify--test-cleanup))
680 (unwind-protect
681 ;; Check copy of files inside a directory.
682 (let ((file-notify--test-tmpdir
683 (make-temp-file "file-notify-test-parent" t)))
684 (should
685 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
686 file-notify--test-tmpfile1 (file-notify--test-make-temp-name)
687 file-notify--test-desc
688 (file-notify-add-watch
689 file-notify--test-tmpdir
690 '(change) #'file-notify--test-event-handler)))
691 (file-notify--test-with-events
692 (cond
693 ;; w32notify does not distinguish between `changed' and
694 ;; `attribute-changed'. It does not raise `deleted' and
695 ;; `stopped' events for the watched directory.
696 ((string-equal (file-notify--test-library) "w32notify")
697 '(created changed created changed
698 changed changed changed
699 deleted deleted))
700 ;; gvfs-monitor-dir on cygwin does not detect the
701 ;; `created' event reliably.
702 ((string-equal
703 (file-notify--test-library) "gvfs-monitor-dir.exe")
704 '((deleted stopped)
705 (created created deleted stopped)))
706 ;; There are three `deleted' events, for two files and
707 ;; for the directory. Except for cygwin and kqueue.
708 ((eq system-type 'cygwin)
709 '(created created changed changed deleted stopped))
710 ((string-equal (file-notify--test-library) "kqueue")
711 '(created changed created changed deleted stopped))
712 (t '(created changed created changed
713 deleted deleted deleted stopped)))
714 (write-region
715 "any text" nil file-notify--test-tmpfile nil 'no-message)
716 (file-notify--test-read-event)
717 (copy-file file-notify--test-tmpfile file-notify--test-tmpfile1)
718 ;; The next two events shall not be visible.
719 (file-notify--test-read-event)
720 (set-file-modes file-notify--test-tmpfile 000)
721 (file-notify--test-read-event)
722 (set-file-times file-notify--test-tmpfile '(0 0))
723 (file-notify--test-read-event)
724 (delete-directory file-notify--test-tmpdir 'recursive))
725 (file-notify-rm-watch file-notify--test-desc)
727 ;; The environment shall be cleaned up.
728 (file-notify--test-cleanup-p))
730 ;; Cleanup.
731 (file-notify--test-cleanup))
733 (unwind-protect
734 ;; Check rename of files inside a directory.
735 (let ((file-notify--test-tmpdir
736 (make-temp-file "file-notify-test-parent" t)))
737 (should
738 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
739 file-notify--test-tmpfile1 (file-notify--test-make-temp-name)
740 file-notify--test-desc
741 (file-notify-add-watch
742 file-notify--test-tmpdir
743 '(change) #'file-notify--test-event-handler)))
744 (file-notify--test-with-events
745 (cond
746 ;; w32notify does not raise `deleted' and `stopped'
747 ;; events for the watched directory.
748 ((string-equal (file-notify--test-library) "w32notify")
749 '(created changed renamed deleted))
750 ;; gvfs-monitor-dir on cygwin does not detect the
751 ;; `created' event reliably.
752 ((string-equal
753 (file-notify--test-library) "gvfs-monitor-dir.exe")
754 '((deleted stopped)
755 (created deleted stopped)))
756 ;; There are two `deleted' events, for the file and for
757 ;; the directory. Except for cygwin and kqueue. And
758 ;; cygwin raises `created' and `deleted' events instead
759 ;; of a `renamed' event.
760 ((eq system-type 'cygwin)
761 '(created created deleted deleted stopped))
762 ((string-equal (file-notify--test-library) "kqueue")
763 '(created changed renamed deleted stopped))
764 (t '(created changed renamed deleted deleted stopped)))
765 (write-region
766 "any text" nil file-notify--test-tmpfile nil 'no-message)
767 (file-notify--test-read-event)
768 (rename-file file-notify--test-tmpfile file-notify--test-tmpfile1)
769 ;; After the rename, we won't get events anymore.
770 (file-notify--test-read-event)
771 (delete-directory file-notify--test-tmpdir 'recursive))
772 (file-notify-rm-watch file-notify--test-desc)
774 ;; The environment shall be cleaned up.
775 (file-notify--test-cleanup-p))
777 ;; Cleanup.
778 (file-notify--test-cleanup))
780 (unwind-protect
781 ;; Check attribute change. Does not work for cygwin.
782 (unless (eq system-type 'cygwin)
783 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
784 (write-region
785 "any text" nil file-notify--test-tmpfile nil 'no-message)
786 (should
787 (setq file-notify--test-desc
788 (file-notify-add-watch
789 file-notify--test-tmpfile
790 '(attribute-change) #'file-notify--test-event-handler)))
791 (file-notify--test-with-events
792 (cond
793 ;; w32notify does not distinguish between `changed' and
794 ;; `attribute-changed'. Under MS Windows 7, we get four
795 ;; `changed' events, and under MS Windows 10 just two.
796 ;; Strange.
797 ((string-equal (file-notify--test-library) "w32notify")
798 '((changed changed)
799 (changed changed changed changed)))
800 ;; For kqueue and in the remote case, `write-region'
801 ;; raises also an `attribute-changed' event.
802 ((or (string-equal (file-notify--test-library) "kqueue")
803 (file-remote-p temporary-file-directory))
804 '(attribute-changed attribute-changed attribute-changed))
805 (t '(attribute-changed attribute-changed)))
806 (write-region
807 "any text" nil file-notify--test-tmpfile nil 'no-message)
808 (file-notify--test-read-event)
809 (set-file-modes file-notify--test-tmpfile 000)
810 (file-notify--test-read-event)
811 (set-file-times file-notify--test-tmpfile '(0 0))
812 (file-notify--test-read-event)
813 (delete-file file-notify--test-tmpfile))
814 (file-notify-rm-watch file-notify--test-desc)
816 ;; The environment shall be cleaned up.
817 (file-notify--test-cleanup-p))
819 ;; Cleanup.
820 (file-notify--test-cleanup)))
822 (file-notify--deftest-remote file-notify-test03-events
823 "Check file creation/change/removal notifications for remote files.")
825 (require 'autorevert)
826 (setq auto-revert-notify-exclude-dir-regexp "nothing-to-be-excluded"
827 auto-revert-remote-files t
828 auto-revert-stop-on-user-input nil)
830 (ert-deftest file-notify-test04-autorevert ()
831 "Check autorevert via file notification."
832 (skip-unless (file-notify--test-local-enabled))
834 ;; `auto-revert-buffers' runs every 5". And we must wait, until the
835 ;; file has been reverted.
836 (let ((timeout (if (file-remote-p temporary-file-directory) 60 10))
837 buf)
838 (unwind-protect
839 (progn
840 ;; In the remote case, `vc-refresh-state' returns undesired
841 ;; error messages. Let's suppress them.
842 (advice-add 'vc-refresh-state :around 'ignore)
843 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
844 (write-region
845 "any text" nil file-notify--test-tmpfile nil 'no-message)
846 (setq buf (find-file-noselect file-notify--test-tmpfile))
847 (with-current-buffer buf
848 (should (string-equal (buffer-string) "any text"))
849 ;; `buffer-stale--default-function' checks for
850 ;; `verify-visited-file-modtime'. We must ensure that it
851 ;; returns nil.
852 (sleep-for 1)
853 (auto-revert-mode 1)
855 ;; `auto-revert-buffers' runs every 5".
856 (with-timeout (timeout (ignore))
857 (while (null auto-revert-notify-watch-descriptor)
858 (sleep-for 1)))
860 ;; `file-notify--test-monitor' needs to know
861 ;; `file-notify--test-desc' in order to compute proper
862 ;; timeouts.
863 (setq file-notify--test-desc auto-revert-notify-watch-descriptor)
865 ;; Check, that file notification has been used.
866 (should auto-revert-mode)
867 (should auto-revert-use-notify)
868 (should auto-revert-notify-watch-descriptor)
870 ;; Modify file. We wait for a second, in order to have
871 ;; another timestamp.
872 (ert-with-message-capture captured-messages
873 (sleep-for 1)
874 (write-region
875 "another text" nil file-notify--test-tmpfile nil 'no-message)
877 ;; Check, that the buffer has been reverted.
878 (file-notify--wait-for-events
879 timeout
880 (string-match
881 (format-message "Reverting buffer `%s'." (buffer-name buf))
882 captured-messages))
883 (should (string-match "another text" (buffer-string))))
885 ;; Stop file notification. Autorevert shall still work via polling.
886 (file-notify-rm-watch auto-revert-notify-watch-descriptor)
887 (file-notify--wait-for-events
888 timeout (null auto-revert-notify-watch-descriptor))
889 (should auto-revert-use-notify)
890 (should-not auto-revert-notify-watch-descriptor)
892 ;; Modify file. We wait for two seconds, in order to
893 ;; have another timestamp. One second seems to be too
894 ;; short.
895 (ert-with-message-capture captured-messages
896 (sleep-for 2)
897 (write-region
898 "foo bla" nil file-notify--test-tmpfile nil 'no-message)
900 ;; Check, that the buffer has been reverted.
901 (file-notify--wait-for-events
902 timeout
903 (string-match
904 (format-message "Reverting buffer `%s'." (buffer-name buf))
905 captured-messages))
906 (should (string-match "foo bla" (buffer-string))))
908 ;; Stop autorevert, in order to cleanup descriptor.
909 (auto-revert-mode -1))
911 ;; The environment shall be cleaned up.
912 (file-notify--test-cleanup-p))
914 ;; Cleanup.
915 (advice-remove 'vc-refresh-state 'ignore)
916 (ignore-errors (kill-buffer buf))
917 (file-notify--test-cleanup))))
919 (file-notify--deftest-remote file-notify-test04-autorevert
920 "Check autorevert via file notification for remote files.")
922 (ert-deftest file-notify-test05-file-validity ()
923 "Check `file-notify-valid-p' for files."
924 (skip-unless (file-notify--test-local-enabled))
926 (unwind-protect
927 (progn
928 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
929 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
930 (should
931 (setq file-notify--test-desc
932 (file-notify-add-watch
933 file-notify--test-tmpfile '(change) #'ignore)))
934 (should (file-notify-valid-p file-notify--test-desc))
935 ;; After calling `file-notify-rm-watch', the descriptor is not
936 ;; valid anymore.
937 (file-notify-rm-watch file-notify--test-desc)
938 (should-not (file-notify-valid-p file-notify--test-desc))
939 (delete-file file-notify--test-tmpfile)
941 ;; The environment shall be cleaned up.
942 (file-notify--test-cleanup-p))
944 ;; Cleanup.
945 (file-notify--test-cleanup))
947 (unwind-protect
948 (progn
949 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
950 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
951 (should
952 (setq file-notify--test-desc
953 (file-notify-add-watch
954 file-notify--test-tmpfile
955 '(change) #'file-notify--test-event-handler)))
956 (should (file-notify-valid-p file-notify--test-desc))
957 (file-notify--test-with-events
958 (cond
959 ;; gvfs-monitor-dir on cygwin does not detect the
960 ;; `changed' event reliably.
961 ((string-equal (file-notify--test-library) "gvfs-monitor-dir.exe")
962 '((deleted stopped)
963 (changed deleted stopped)))
964 ;; There could be one or two `changed' events.
965 (t '((changed deleted stopped)
966 (changed changed deleted stopped))))
967 (write-region
968 "another text" nil file-notify--test-tmpfile nil 'no-message)
969 (file-notify--test-read-event)
970 (delete-file file-notify--test-tmpfile))
971 ;; After deleting the file, the descriptor is not valid anymore.
972 (should-not (file-notify-valid-p file-notify--test-desc))
973 (file-notify-rm-watch file-notify--test-desc)
975 ;; The environment shall be cleaned up.
976 (file-notify--test-cleanup-p))
978 ;; Cleanup.
979 (file-notify--test-cleanup))
981 (unwind-protect
982 (let ((file-notify--test-tmpdir
983 (make-temp-file "file-notify-test-parent" t)))
984 (should
985 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
986 file-notify--test-desc
987 (file-notify-add-watch
988 file-notify--test-tmpdir
989 '(change) #'file-notify--test-event-handler)))
990 (should (file-notify-valid-p file-notify--test-desc))
991 (file-notify--test-with-events
992 (cond
993 ;; w32notify does not raise `deleted' and `stopped' events
994 ;; for the watched directory.
995 ((string-equal (file-notify--test-library) "w32notify")
996 '(created changed deleted))
997 ;; gvfs-monitor-dir on cygwin does not detect the `created'
998 ;; event reliably.
999 ((string-equal (file-notify--test-library) "gvfs-monitor-dir.exe")
1000 '((deleted stopped)
1001 (created deleted stopped)))
1002 ;; There are two `deleted' events, for the file and for the
1003 ;; directory. Except for cygwin and kqueue. And cygwin
1004 ;; does not raise a `changed' event.
1005 ((eq system-type 'cygwin)
1006 '(created deleted stopped))
1007 ((string-equal (file-notify--test-library) "kqueue")
1008 '(created changed deleted stopped))
1009 (t '(created changed deleted deleted stopped)))
1010 (write-region
1011 "any text" nil file-notify--test-tmpfile nil 'no-message)
1012 (file-notify--test-read-event)
1013 (delete-directory file-notify--test-tmpdir 'recursive))
1014 ;; After deleting the parent directory, the descriptor must
1015 ;; not be valid anymore.
1016 (should-not (file-notify-valid-p file-notify--test-desc))
1017 ;; w32notify doesn't generate `stopped' events when the parent
1018 ;; directory is deleted, which doesn't provide a chance for
1019 ;; filenotify.el to remove the descriptor from the internal
1020 ;; hash table it maintains. So we must remove the descriptor
1021 ;; manually.
1022 (if (string-equal (file-notify--test-library) "w32notify")
1023 (file-notify--rm-descriptor file-notify--test-desc))
1025 ;; The environment shall be cleaned up.
1026 (file-notify--test-cleanup-p))
1028 ;; Cleanup.
1029 (file-notify--test-cleanup)))
1031 (file-notify--deftest-remote file-notify-test05-file-validity
1032 "Check `file-notify-valid-p' via file notification for remote files.")
1034 (ert-deftest file-notify-test06-dir-validity ()
1035 "Check `file-notify-valid-p' for directories."
1036 (skip-unless (file-notify--test-local-enabled))
1038 (unwind-protect
1039 (progn
1040 (should
1041 (setq file-notify--test-tmpfile
1042 (make-temp-file "file-notify-test-parent" t)))
1043 (should
1044 (setq file-notify--test-desc
1045 (file-notify-add-watch
1046 file-notify--test-tmpfile '(change) #'ignore)))
1047 (should (file-notify-valid-p file-notify--test-desc))
1048 ;; After removing the watch, the descriptor must not be valid
1049 ;; anymore.
1050 (file-notify-rm-watch file-notify--test-desc)
1051 (file-notify--wait-for-events
1052 (file-notify--test-timeout)
1053 (not (file-notify-valid-p file-notify--test-desc)))
1054 (should-not (file-notify-valid-p file-notify--test-desc))
1055 (delete-directory file-notify--test-tmpfile 'recursive)
1057 ;; The environment shall be cleaned up.
1058 (file-notify--test-cleanup-p))
1060 ;; Cleanup.
1061 (file-notify--test-cleanup))
1063 (unwind-protect
1064 (progn
1065 (should
1066 (setq file-notify--test-tmpfile
1067 (make-temp-file "file-notify-test-parent" t)))
1068 (should
1069 (setq file-notify--test-desc
1070 (file-notify-add-watch
1071 file-notify--test-tmpfile '(change) #'ignore)))
1072 (should (file-notify-valid-p file-notify--test-desc))
1073 ;; After deleting the directory, the descriptor must not be
1074 ;; valid anymore.
1075 (delete-directory file-notify--test-tmpfile 'recursive)
1076 (file-notify--wait-for-events
1077 (file-notify--test-timeout)
1078 (not (file-notify-valid-p file-notify--test-desc)))
1079 (should-not (file-notify-valid-p file-notify--test-desc))
1080 (if (string-equal (file-notify--test-library) "w32notify")
1081 (file-notify--rm-descriptor file-notify--test-desc))
1083 ;; The environment shall be cleaned up.
1084 (file-notify--test-cleanup-p))
1086 ;; Cleanup.
1087 (file-notify--test-cleanup)))
1089 (file-notify--deftest-remote file-notify-test06-dir-validity
1090 "Check `file-notify-valid-p' via file notification for remote directories.")
1092 (ert-deftest file-notify-test07-many-events ()
1093 "Check that events are not dropped."
1094 :tags '(:expensive-test)
1095 (skip-unless (file-notify--test-local-enabled))
1097 (should
1098 (setq file-notify--test-tmpfile
1099 (make-temp-file "file-notify-test-parent" t)))
1100 (should
1101 (setq file-notify--test-desc
1102 (file-notify-add-watch
1103 file-notify--test-tmpfile
1104 '(change) #'file-notify--test-event-handler)))
1105 (unwind-protect
1106 (let ((n 1000)
1107 source-file-list target-file-list
1108 (default-directory file-notify--test-tmpfile))
1109 (dotimes (i n)
1110 ;; It matters which direction we rename, at least for
1111 ;; kqueue. This backend parses directories in alphabetic
1112 ;; order (x%d before y%d). So we rename into both directions.
1113 (if (zerop (mod i 2))
1114 (progn
1115 (push (expand-file-name (format "x%d" i)) source-file-list)
1116 (push (expand-file-name (format "y%d" i)) target-file-list))
1117 (push (expand-file-name (format "y%d" i)) source-file-list)
1118 (push (expand-file-name (format "x%d" i)) target-file-list)))
1119 (file-notify--test-with-events (make-list (+ n n) 'created)
1120 (let ((source-file-list source-file-list)
1121 (target-file-list target-file-list))
1122 (while (and source-file-list target-file-list)
1123 (file-notify--test-read-event)
1124 (write-region "" nil (pop source-file-list) nil 'no-message)
1125 (file-notify--test-read-event)
1126 (write-region "" nil (pop target-file-list) nil 'no-message))))
1127 (file-notify--test-with-events
1128 (cond
1129 ;; w32notify fires both `deleted' and `renamed' events.
1130 ((string-equal (file-notify--test-library) "w32notify")
1131 (let (r)
1132 (dotimes (_i n)
1133 (setq r (append '(deleted renamed) r)))
1135 ;; cygwin fires `changed' and `deleted' events, sometimes
1136 ;; in random order.
1137 ((eq system-type 'cygwin)
1138 (let (r)
1139 (dotimes (_i n)
1140 (setq r (append '(changed deleted) r)))
1141 (cons :random r)))
1142 (t (make-list n 'renamed)))
1143 (let ((source-file-list source-file-list)
1144 (target-file-list target-file-list))
1145 (while (and source-file-list target-file-list)
1146 (file-notify--test-read-event)
1147 (rename-file (pop source-file-list) (pop target-file-list) t))))
1148 (file-notify--test-with-events (make-list n 'deleted)
1149 (dolist (file target-file-list)
1150 (file-notify--test-read-event)
1151 (delete-file file)))
1152 (delete-directory file-notify--test-tmpfile)
1153 (if (string-equal (file-notify--test-library) "w32notify")
1154 (file-notify--rm-descriptor file-notify--test-desc))
1156 ;; The environment shall be cleaned up.
1157 (file-notify--test-cleanup-p))
1159 ;; Cleanup.
1160 (file-notify--test-cleanup)))
1162 (file-notify--deftest-remote file-notify-test07-many-events
1163 "Check that events are not dropped for remote directories.")
1165 (ert-deftest file-notify-test08-backup ()
1166 "Check that backup keeps file notification."
1167 (skip-unless (file-notify--test-local-enabled))
1169 (unwind-protect
1170 (progn
1171 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
1172 (write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
1173 (should
1174 (setq file-notify--test-desc
1175 (file-notify-add-watch
1176 file-notify--test-tmpfile
1177 '(change) #'file-notify--test-event-handler)))
1178 (should (file-notify-valid-p file-notify--test-desc))
1179 (file-notify--test-with-events
1180 ;; There could be one or two `changed' events.
1181 '((changed)
1182 (changed changed))
1183 ;; There shouldn't be any problem, because the file is kept.
1184 (with-temp-buffer
1185 (let ((buffer-file-name file-notify--test-tmpfile)
1186 (make-backup-files t)
1187 (backup-by-copying t)
1188 (kept-new-versions 1)
1189 (delete-old-versions t))
1190 (insert "another text")
1191 (save-buffer))))
1192 ;; After saving the buffer, the descriptor is still valid.
1193 (should (file-notify-valid-p file-notify--test-desc))
1194 (delete-file file-notify--test-tmpfile)
1196 ;; The environment shall be cleaned up.
1197 (file-notify--test-cleanup-p))
1199 ;; Cleanup.
1200 (file-notify--test-cleanup))
1202 (unwind-protect
1203 ;; It doesn't work for kqueue, because we don't use an implicit
1204 ;; directory monitor.
1205 (unless (string-equal (file-notify--test-library) "kqueue")
1206 (setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
1207 (write-region
1208 "any text" nil file-notify--test-tmpfile nil 'no-message)
1209 (should
1210 (setq file-notify--test-desc
1211 (file-notify-add-watch
1212 file-notify--test-tmpfile
1213 '(change) #'file-notify--test-event-handler)))
1214 (should (file-notify-valid-p file-notify--test-desc))
1215 (file-notify--test-with-events
1216 (cond
1217 ;; On cygwin we only get the `changed' event.
1218 ((eq system-type 'cygwin) '(changed))
1219 (t '(renamed created changed)))
1220 ;; The file is renamed when creating a backup. It shall
1221 ;; still be watched.
1222 (with-temp-buffer
1223 (let ((buffer-file-name file-notify--test-tmpfile)
1224 (make-backup-files t)
1225 (backup-by-copying nil)
1226 (backup-by-copying-when-mismatch nil)
1227 (kept-new-versions 1)
1228 (delete-old-versions t))
1229 (insert "another text")
1230 (save-buffer))))
1231 ;; After saving the buffer, the descriptor is still valid.
1232 (should (file-notify-valid-p file-notify--test-desc))
1233 (delete-file file-notify--test-tmpfile)
1235 ;; The environment shall be cleaned up.
1236 (file-notify--test-cleanup-p))
1238 ;; Cleanup.
1239 (file-notify--test-cleanup)))
1241 (file-notify--deftest-remote file-notify-test08-backup
1242 "Check that backup keeps file notification for remote files.")
1244 (ert-deftest file-notify-test09-watched-file-in-watched-dir ()
1245 "Watches a directory and a file in that directory separately.
1246 Checks that the callbacks are only called with events with
1247 descriptors that were issued when registering the watches. This
1248 test caters for the situation in bug#22736 where the callback for
1249 the directory received events for the file with the descriptor of
1250 the file watch."
1251 :tags '(:expensive-test)
1252 (skip-unless (file-notify--test-local-enabled))
1254 ;; A directory to be watched.
1255 (should
1256 (setq file-notify--test-tmpfile
1257 (make-temp-file "file-notify-test-parent" t)))
1258 ;; A file to be watched.
1259 (should
1260 (setq file-notify--test-tmpfile1
1261 (let ((file-notify--test-tmpdir file-notify--test-tmpfile))
1262 (file-notify--test-make-temp-name))))
1263 (write-region "any text" nil file-notify--test-tmpfile1 nil 'no-message)
1264 (unwind-protect
1265 (cl-flet (;; Directory monitor.
1266 (dir-callback (event)
1267 (let ((file-notify--test-desc file-notify--test-desc1))
1268 (file-notify--test-event-handler event)))
1269 ;; File monitor.
1270 (file-callback (event)
1271 (let ((file-notify--test-desc file-notify--test-desc2))
1272 (file-notify--test-event-handler event))))
1273 (should
1274 (setq file-notify--test-desc1
1275 (file-notify-add-watch
1276 file-notify--test-tmpfile
1277 '(change) #'dir-callback)
1278 ;; This is needed for `file-notify--test-monitor'.
1279 file-notify--test-desc file-notify--test-desc1))
1280 (should
1281 (setq file-notify--test-desc2
1282 (file-notify-add-watch
1283 file-notify--test-tmpfile1
1284 '(change) #'file-callback)))
1285 (should (file-notify-valid-p file-notify--test-desc1))
1286 (should (file-notify-valid-p file-notify--test-desc2))
1287 (should-not (equal file-notify--test-desc1 file-notify--test-desc2))
1288 (let ((n 100))
1289 ;; Run the test.
1290 (file-notify--test-with-events
1291 ;; There could be one or two `changed' events.
1292 (list
1293 ;; cygwin.
1294 (append
1295 '(:random)
1296 (make-list (/ n 2) 'changed)
1297 (make-list (/ n 2) 'created)
1298 (make-list (/ n 2) 'changed))
1299 (append
1300 '(:random)
1301 ;; Directory monitor and file monitor.
1302 (make-list (/ n 2) 'changed)
1303 (make-list (/ n 2) 'changed)
1304 ;; Just the directory monitor.
1305 (make-list (/ n 2) 'created)
1306 (make-list (/ n 2) 'changed))
1307 (append
1308 '(:random)
1309 ;; Directory monitor and file monitor.
1310 (make-list (/ n 2) 'changed)
1311 (make-list (/ n 2) 'changed)
1312 (make-list (/ n 2) 'changed)
1313 (make-list (/ n 2) 'changed)
1314 ;; Just the directory monitor.
1315 (make-list (/ n 2) 'created)
1316 (make-list (/ n 2) 'changed)))
1317 (dotimes (i n)
1318 (file-notify--test-read-event)
1319 (if (zerop (mod i 2))
1320 (write-region
1321 "any text" nil file-notify--test-tmpfile1 t 'no-message)
1322 (let ((file-notify--test-tmpdir file-notify--test-tmpfile))
1323 (write-region
1324 "any text" nil
1325 (file-notify--test-make-temp-name) nil 'no-message))))))
1327 ;; If we delete the file, the directory monitor shall still be
1328 ;; active. We receive the `deleted' event from both the
1329 ;; directory and the file monitor. The `stopped' event is
1330 ;; from the file monitor. It's undecided in which order the
1331 ;; the directory and the file monitor are triggered.
1332 (file-notify--test-with-events '(:random deleted deleted stopped)
1333 (delete-file file-notify--test-tmpfile1))
1334 (should (file-notify-valid-p file-notify--test-desc1))
1335 (should-not (file-notify-valid-p file-notify--test-desc2))
1337 ;; Now we delete the directory.
1338 (file-notify--test-with-events
1339 (cond
1340 ;; In kqueue and for cygwin, just one `deleted' event for
1341 ;; the directory is received.
1342 ((or (eq system-type 'cygwin)
1343 (string-equal (file-notify--test-library) "kqueue"))
1344 '(deleted stopped))
1345 (t (append
1346 ;; The directory monitor raises a `deleted' event for
1347 ;; every file contained in the directory, we must
1348 ;; count them.
1349 (make-list
1350 (length
1351 (directory-files
1352 file-notify--test-tmpfile nil
1353 directory-files-no-dot-files-regexp 'nosort))
1354 'deleted)
1355 ;; The events of the directory itself.
1356 (cond
1357 ;; w32notify does not raise `deleted' and `stopped'
1358 ;; events for the watched directory.
1359 ((string-equal (file-notify--test-library) "w32notify") '())
1360 (t '(deleted stopped))))))
1361 (delete-directory file-notify--test-tmpfile 'recursive))
1362 (should-not (file-notify-valid-p file-notify--test-desc1))
1363 (should-not (file-notify-valid-p file-notify--test-desc2))
1364 (when (string-equal (file-notify--test-library) "w32notify")
1365 (file-notify--rm-descriptor file-notify--test-desc1)
1366 (file-notify--rm-descriptor file-notify--test-desc2))
1368 ;; The environment shall be cleaned up.
1369 (file-notify--test-cleanup-p))
1371 ;; Cleanup.
1372 (file-notify--test-cleanup)))
1374 ;(file-notify--deftest-remote file-notify-test09-watched-file-in-watched-dir
1375 ; "Check `file-notify-test09-watched-file-in-watched-dir' for remote files.")
1377 (ert-deftest file-notify-test10-sufficient-resources ()
1378 "Check that file notification does not use too many resources."
1379 :tags '(:expensive-test)
1380 (skip-unless (file-notify--test-local-enabled))
1381 ;; This test is intended for kqueue only.
1382 (skip-unless (string-equal (file-notify--test-library) "kqueue"))
1384 (should
1385 (setq file-notify--test-tmpfile
1386 (make-temp-file "file-notify-test-parent" t)))
1387 (unwind-protect
1388 (let ((file-notify--test-tmpdir file-notify--test-tmpfile)
1389 descs)
1390 (should-error
1391 (while t
1392 ;; We watch directories, because we want to reach the upper
1393 ;; limit. Watching a file might not be sufficient, because
1394 ;; most of the libraries implement this as watching the
1395 ;; upper directory.
1396 (setq file-notify--test-tmpfile1
1397 (make-temp-file "file-notify-test-parent" t)
1398 descs
1399 (cons
1400 (should
1401 (file-notify-add-watch
1402 file-notify--test-tmpfile1 '(change) #'ignore))
1403 descs)))
1404 :type 'file-notify-error)
1405 ;; Remove watches. If we don't do it prior removing
1406 ;; directories, Emacs crashes in batch mode.
1407 (dolist (desc descs)
1408 (file-notify-rm-watch desc))
1409 ;; Remove directories.
1410 (delete-directory file-notify--test-tmpfile 'recursive)
1412 ;; The environment shall be cleaned up.
1413 (file-notify--test-cleanup-p))
1415 ;; Cleanup.
1416 (file-notify--test-cleanup)))
1418 (file-notify--deftest-remote file-notify-test10-sufficient-resources
1419 "Check `file-notify-test10-sufficient-resources' for remote files.")
1421 (defun file-notify-test-all (&optional interactive)
1422 "Run all tests for \\[file-notify]."
1423 (interactive "p")
1424 (if interactive
1425 (ert-run-tests-interactively "^file-notify-")
1426 (ert-run-tests-batch "^file-notify-")))
1428 ;; TODO:
1430 ;; * kqueue does not send all expected `deleted' events. Maybe due to
1431 ;; the missing directory monitor.
1432 ;; * For w32notify, no `deleted' and `stopped' events arrive when a
1433 ;; directory is removed.
1434 ;; * For cygwin and w32notify, no `attribute-changed' events arrive.
1435 ;; They send `changed' events instead.
1436 ;; * cygwin does not send all expected `changed' and `deleted' events.
1437 ;; Probably due to timing issues.
1439 (provide 'file-notify-tests)
1440 ;;; filenotify-tests.el ends here