mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-06 06:20:55 -08:00
* test/lisp/autorevert-tests.el (auto-revert-test05-global-notify) (auto-revert-test07-auto-revert-several-buffers): Adapt tests. * test/src/comp-resources/comp-test-funcs.el (foo): Define error symbol.
883 lines
37 KiB
EmacsLisp
883 lines
37 KiB
EmacsLisp
;;; autorevert-tests.el --- Tests of auto-revert -*- lexical-binding: t -*-
|
|
|
|
;; Copyright (C) 2015-2025 Free Software Foundation, Inc.
|
|
|
|
;; Author: Michael Albinus <michael.albinus@gmx.de>
|
|
|
|
;; This file is part of GNU Emacs.
|
|
;;
|
|
;; GNU Emacs is free software: you can redistribute it and/or
|
|
;; modify it under the terms of the GNU General Public License as
|
|
;; published by the Free Software Foundation, either version 3 of the
|
|
;; License, or (at your option) any later version.
|
|
;;
|
|
;; GNU Emacs is distributed in the hope that it will be useful, but
|
|
;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
;; General Public License for more details.
|
|
;;
|
|
;; You should have received a copy of the GNU General Public License
|
|
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
;;; Commentary:
|
|
|
|
;; Some of the tests require access to a remote host files. Since
|
|
;; this could be problematic, a mock-up connection method "mock" is
|
|
;; used. Emulating a remote connection, it simply calls "sh -i".
|
|
;; Tramp's file name handlers still run, so this test is sufficient
|
|
;; except for connection establishing.
|
|
|
|
;; If you want to test a real Tramp connection, set
|
|
;; $REMOTE_TEMPORARY_FILE_DIRECTORY to a suitable value in order to
|
|
;; overwrite the default value. If you want to skip tests accessing a
|
|
;; remote host, set this environment variable to "/dev/null" or
|
|
;; whatever is appropriate on your system.
|
|
|
|
;; For the remote file-notify library, Tramp checks for the existence
|
|
;; of a respective command. The first command found is used. In
|
|
;; order to use a dedicated one, the environment variable
|
|
;; $REMOTE_FILE_NOTIFY_LIBRARY shall be set, possible values are
|
|
;; "inotifywait", "gio" and "smb-notify".
|
|
|
|
;; Local file-notify libraries are auto-detected during Emacs
|
|
;; configuration. This can be changed with a respective configuration
|
|
;; argument, like
|
|
;;
|
|
;; --with-file-notification=inotify
|
|
;; --with-file-notification=kqueue
|
|
;; --with-file-notification=gfile
|
|
;; --with-file-notification=w32
|
|
|
|
;; A whole test run can be performed calling the command `auto-revert-test-all'.
|
|
|
|
;;; Code:
|
|
|
|
(require 'tramp)
|
|
(require 'ert-x)
|
|
(require 'autorevert)
|
|
|
|
(setq auth-source-cache-expiry nil
|
|
auth-source-save-behavior nil
|
|
auto-revert-debug nil
|
|
auto-revert-notify-exclude-dir-regexp "nothing-to-be-excluded"
|
|
auto-revert-stop-on-user-input nil
|
|
ert-temp-file-suffix ""
|
|
file-notify-debug nil
|
|
password-cache-expiry nil
|
|
remote-file-name-inhibit-cache nil
|
|
tramp-allow-unsafe-temporary-files t
|
|
tramp-cache-read-persistent-data t ;; For auth-sources.
|
|
tramp-verbose 0
|
|
;; When the remote user id is 0, Tramp refuses unsafe temporary files.
|
|
tramp-allow-unsafe-temporary-files
|
|
(or tramp-allow-unsafe-temporary-files noninteractive))
|
|
|
|
(defvar auto-revert--test-rootdir temporary-file-directory)
|
|
(defvar auto-revert--test-monitors nil)
|
|
|
|
(defun auto-revert--timeout ()
|
|
"Time to wait for a message."
|
|
(+ auto-revert-interval 1))
|
|
|
|
(defvar auto-revert--messages nil
|
|
"Used to collect messages issued during a section of a test.")
|
|
|
|
;; Filter suppressed remote file-notify libraries.
|
|
(when (stringp (getenv "REMOTE_FILE_NOTIFY_LIBRARY"))
|
|
(dolist (lib '("inotifywait" "gio" "smb-notify"))
|
|
(unless (string-equal (getenv "REMOTE_FILE_NOTIFY_LIBRARY") lib)
|
|
(add-to-list 'tramp-connection-properties `(nil ,lib nil)))))
|
|
|
|
(defvar auto-revert--test-enabled-remote-checked nil
|
|
"Cached result of `auto-revert--test-enabled-remote'.
|
|
If the function did run, the value is a cons cell, the `cdr'
|
|
being the result.")
|
|
|
|
(defun auto-revert--test-enabled-remote ()
|
|
"Whether remote file access is enabled."
|
|
(unless (consp auto-revert--test-enabled-remote-checked)
|
|
(setq
|
|
auto-revert--test-enabled-remote-checked
|
|
(cons
|
|
t (ignore-errors
|
|
(and
|
|
(file-remote-p ert-remote-temporary-file-directory)
|
|
(file-directory-p ert-remote-temporary-file-directory)
|
|
(file-writable-p ert-remote-temporary-file-directory))))))
|
|
;; Return result.
|
|
(cdr auto-revert--test-enabled-remote-checked))
|
|
|
|
(defun auto-revert--wait-for-revert (buffer)
|
|
"Wait until a message reports reversion of BUFFER.
|
|
This expects `auto-revert--messages' to be bound by
|
|
`ert-with-message-capture' before calling."
|
|
;; This is a central place to check for proper library and monitor.
|
|
(auto-revert--skip-unless-proper-library-and-monitor buffer)
|
|
(let ((text-quoting-style 'grave))
|
|
(auto-revert-test--wait-for
|
|
(lambda () (string-match-p
|
|
(rx bol "Reverting buffer `"
|
|
(literal (buffer-name buffer)) "'" eol)
|
|
(or auto-revert--messages "")))
|
|
(auto-revert--timeout))))
|
|
|
|
(defun auto-revert--test-library ()
|
|
"The used library for the test, as a string.
|
|
In the remote case, it is the process name which runs on the
|
|
remote host, or nil."
|
|
(if (null (file-remote-p auto-revert--test-rootdir))
|
|
(symbol-name file-notify--library)
|
|
(and (processp auto-revert-notify-watch-descriptor)
|
|
(replace-regexp-in-string
|
|
"<[[:digit:]]+>\\'" ""
|
|
(process-name auto-revert-notify-watch-descriptor)))))
|
|
|
|
(defun auto-revert--test-monitor ()
|
|
"The used monitor for the test, as a symbol.
|
|
This returns only for (local) gfilenotify, (remote) gio or (remote)
|
|
smb-notify libraries; otherwise it is nil.
|
|
`auto-revert-notify-watch-descriptor' must be a valid watch descriptor."
|
|
;; We cache the result, because after `file-notify-rm-watch',
|
|
;; `gfile-monitor-name' does not return a proper result anymore.
|
|
;; But we still need this information. So far, we know the monitors
|
|
;; - GFamFileMonitor (gfilenotify on cygwin)
|
|
;; - GFamDirectoryMonitor (gfilenotify on Solaris)
|
|
;; - GInotifyFileMonitor (gfilenotify and gio on GNU/Linux)
|
|
;; - GKqueueFileMonitor (gfilenotify and gio on FreeBSD)
|
|
;; - GPollFileMonitor (gio on cygwin)
|
|
;; - SMBSamba (smb-notify on Samba server)
|
|
;; - SMBWindows (smb-notify on MS Windows).
|
|
(when auto-revert-notify-watch-descriptor
|
|
(or (alist-get
|
|
auto-revert-notify-watch-descriptor auto-revert--test-monitors)
|
|
(when (member
|
|
(auto-revert--test-library) '("gfilenotify" "gio" "smb-notify"))
|
|
(add-to-list
|
|
'auto-revert--test-monitors
|
|
(cons auto-revert-notify-watch-descriptor
|
|
(if (file-remote-p auto-revert--test-rootdir)
|
|
;; `auto-revert-notify-watch-descriptor' is the
|
|
;; connection process.
|
|
(progn
|
|
(while (and (process-live-p
|
|
auto-revert-notify-watch-descriptor)
|
|
(not (tramp-connection-property-p
|
|
auto-revert-notify-watch-descriptor
|
|
"file-monitor")))
|
|
(accept-process-output
|
|
auto-revert-notify-watch-descriptor 0))
|
|
(tramp-get-connection-property
|
|
auto-revert-notify-watch-descriptor "file-monitor"))
|
|
(and (functionp 'gfile-monitor-name)
|
|
(gfile-monitor-name
|
|
auto-revert-notify-watch-descriptor)))))
|
|
;; If we don't know the monitor, there are good chances the
|
|
;; test will fail. We skip it.
|
|
(unless (alist-get
|
|
auto-revert-notify-watch-descriptor auto-revert--test-monitors)
|
|
(ert-skip "Cannot determine test monitor")))
|
|
(alist-get
|
|
auto-revert-notify-watch-descriptor auto-revert--test-monitors))))
|
|
|
|
(defun auto-revert--skip-unless-proper-library-and-monitor (&optional buffer)
|
|
"Skip unless there is no proper file notification library.
|
|
It is checked for buffer-local `auto-revert-notify-watch-descriptor'."
|
|
(with-current-buffer (or buffer (current-buffer))
|
|
(when (eq (auto-revert--test-monitor) 'GKqueueFileMonitor)
|
|
(ert-skip (format "Monitor %s does not support auto-revert"
|
|
(auto-revert--test-monitor))))))
|
|
|
|
(defmacro auto-revert--deftest-remote (test docstring)
|
|
"Define ert `TEST-remote' for remote files."
|
|
(declare (indent 1) (debug (symbolp stringp)))
|
|
`(ert-deftest ,(intern (concat (symbol-name test) "-remote")) ()
|
|
,docstring
|
|
:tags '(:expensive-test)
|
|
(let ((auto-revert--test-rootdir
|
|
ert-remote-temporary-file-directory)
|
|
(auto-revert-remote-files t)
|
|
(ert-test (ert-get-test ',test))
|
|
vc-handled-backends)
|
|
(skip-unless (auto-revert--test-enabled-remote))
|
|
(tramp-cleanup-connection
|
|
(tramp-dissect-file-name auto-revert--test-rootdir) t 'keep-password)
|
|
(condition-case err
|
|
(funcall (ert-test-body ert-test))
|
|
(error (message "%s" err) (signal (car err) (cdr err)))))))
|
|
|
|
(defmacro with-auto-revert-test (&rest body)
|
|
(declare (debug t))
|
|
`(let ((auto-revert-interval-orig auto-revert-interval)
|
|
(auto-revert--lockout-interval-orig auto-revert--lockout-interval)
|
|
(ert-temp-file-prefix
|
|
(expand-file-name "auto-revert-test" auto-revert--test-rootdir)))
|
|
(unwind-protect
|
|
(progn
|
|
(unless (file-remote-p auto-revert--test-rootdir)
|
|
(customize-set-variable 'auto-revert-interval 0.1)
|
|
(setq auto-revert--lockout-interval 0.05))
|
|
,@body)
|
|
(customize-set-variable 'auto-revert-interval auto-revert-interval-orig)
|
|
(setq auto-revert--lockout-interval auto-revert--lockout-interval-orig)
|
|
(ignore-errors (file-notify-rm-all-watches))
|
|
(sleep-for 1))))
|
|
|
|
(defun auto-revert-test--write-region (string file &optional append)
|
|
"Write STRING to FILE."
|
|
(write-region string nil file append 'no-message))
|
|
|
|
(defun auto-revert-test--write-file (string file &optional append)
|
|
"Write STRING to FILE.
|
|
Set modified file time in order to trigger auto-revert."
|
|
(auto-revert-test--write-region string file append)
|
|
(set-file-times file (time-subtract nil (seconds-to-time 60))))
|
|
|
|
(ert-deftest auto-revert-test00-auto-revert-mode ()
|
|
"Check autorevert for a file."
|
|
;; `auto-revert-buffers' runs every 5". And we must wait, until the
|
|
;; file has been reverted.
|
|
(with-auto-revert-test
|
|
(ert-with-temp-file tmpfile
|
|
:prefix ert-temp-file-prefix
|
|
(let (buf)
|
|
(unwind-protect
|
|
(progn
|
|
(auto-revert-test--write-region "any text" tmpfile)
|
|
(setq buf (find-file-noselect tmpfile))
|
|
(with-current-buffer buf
|
|
(ert-with-message-capture auto-revert--messages
|
|
(should
|
|
(string-equal
|
|
(substring-no-properties (buffer-string)) "any text"))
|
|
;; `buffer-stale--default-function' checks for
|
|
;; `verify-visited-file-modtime'. We must ensure that it
|
|
;; returns nil. We also clean up.
|
|
(auto-revert-mode 1)
|
|
(should auto-revert-mode)
|
|
;; There was already an initial call of
|
|
;; `auto-revert-handler', which locks file
|
|
;; notification. Reset this lock.
|
|
(setq auto-revert--last-time 0)
|
|
(auto-revert-test--wait-for
|
|
(lambda () (null auto-revert--lockout-timer))
|
|
(auto-revert--timeout))
|
|
|
|
(auto-revert-test--write-file "another text" tmpfile)
|
|
|
|
;; Check, that the buffer has been reverted.
|
|
(auto-revert--wait-for-revert buf))
|
|
(should
|
|
(string-match-p
|
|
"another text" (substring-no-properties (buffer-string))))
|
|
|
|
;; When the buffer is modified, it shall not be reverted.
|
|
(ert-with-message-capture auto-revert--messages
|
|
(set-buffer-modified-p t)
|
|
(auto-revert-test--write-file "any text" tmpfile)
|
|
|
|
;; Check, that the buffer hasn't been reverted.
|
|
(auto-revert--wait-for-revert buf))
|
|
(should-not
|
|
(string-match-p
|
|
"any text" (substring-no-properties (buffer-string))))))
|
|
|
|
;; Exit.
|
|
(ignore-errors
|
|
(with-current-buffer buf (set-buffer-modified-p nil))
|
|
(kill-buffer buf)))))))
|
|
|
|
(auto-revert--deftest-remote auto-revert-test00-auto-revert-mode
|
|
"Check autorevert for a remote file.")
|
|
|
|
;; This is inspired by Bug#21841.
|
|
(ert-deftest auto-revert-test01-auto-revert-several-files ()
|
|
"Check autorevert for several files at once."
|
|
(let ((default-directory auto-revert--test-rootdir))
|
|
(skip-unless
|
|
(executable-find "cp" (file-remote-p auto-revert--test-rootdir))))
|
|
|
|
(with-auto-revert-test
|
|
(ert-with-temp-directory tmpdir1
|
|
:prefix (concat ert-temp-file-prefix "-parent")
|
|
(ert-with-temp-directory tmpdir2
|
|
:prefix (concat ert-temp-file-prefix "-parent")
|
|
(ert-with-temp-file tmpfile1
|
|
:prefix (expand-file-name "auto-revert-test" tmpdir1)
|
|
(ert-with-temp-file tmpfile2
|
|
:prefix (expand-file-name "auto-revert-test" tmpdir2)
|
|
(let (buf1 buf2)
|
|
(unwind-protect
|
|
(ert-with-message-capture auto-revert--messages
|
|
(auto-revert-test--write-region "any text" tmpfile1)
|
|
(setq buf1 (find-file-noselect tmpfile1))
|
|
(auto-revert-test--write-region "any text" tmpfile2)
|
|
(setq buf2 (find-file-noselect tmpfile2))
|
|
|
|
(dolist (buf (list buf1 buf2))
|
|
(with-current-buffer buf
|
|
(should
|
|
(string-equal
|
|
(substring-no-properties (buffer-string)) "any text"))
|
|
;; `buffer-stale--default-function' checks for
|
|
;; `verify-visited-file-modtime'. We must ensure that
|
|
;; it returns nil.
|
|
(auto-revert-mode 1)
|
|
(should auto-revert-mode)))
|
|
|
|
;; Modify files. We wait for a second, in order to have
|
|
;; another timestamp.
|
|
;; `tmpdir2' is not under auto-revert.
|
|
(auto-revert-test--write-file
|
|
"another text"
|
|
(expand-file-name (file-name-nondirectory tmpfile1) tmpdir2))
|
|
(auto-revert-test--write-file
|
|
"another text"
|
|
(expand-file-name (file-name-nondirectory tmpfile2) tmpdir2))
|
|
|
|
;; (copy-directory tmpdir2 tmpdir1 'keep 'copy-contents)
|
|
;; Strange, that `copy-directory' does not work as
|
|
;; expected. The following shell command is not
|
|
;; portable on all platforms, unfortunately.
|
|
(let ((default-directory auto-revert--test-rootdir))
|
|
(shell-command
|
|
(format "%s -pf %s/* %s"
|
|
(executable-find "cp" t)
|
|
(file-local-name tmpdir2)
|
|
(file-local-name tmpdir1))))
|
|
|
|
;; Check, that the buffers have been reverted.
|
|
(dolist (buf (list buf1 buf2))
|
|
(with-current-buffer buf
|
|
(auto-revert--wait-for-revert buf)
|
|
(should
|
|
(string-match-p
|
|
"another text"
|
|
(substring-no-properties (buffer-string)))))))
|
|
|
|
;; Exit.
|
|
(ignore-errors
|
|
(dolist (buf (list buf1 buf2))
|
|
(with-current-buffer buf (set-buffer-modified-p nil))
|
|
(kill-buffer buf)))))))))))
|
|
|
|
(auto-revert--deftest-remote auto-revert-test01-auto-revert-several-files
|
|
"Check autorevert for several remote files at once.")
|
|
|
|
;; This is inspired by Bug#23276.
|
|
(ert-deftest auto-revert-test02-auto-revert-deleted-file ()
|
|
"Check autorevert for a deleted file."
|
|
(with-auto-revert-test
|
|
(ert-with-temp-file tmpfile
|
|
:prefix ert-temp-file-prefix
|
|
(let (buf)
|
|
(unwind-protect
|
|
(progn
|
|
(auto-revert-test--write-region "any text" tmpfile)
|
|
(setq buf (find-file-noselect tmpfile))
|
|
(with-current-buffer buf
|
|
(should-not
|
|
(file-notify-valid-p auto-revert-notify-watch-descriptor))
|
|
(should
|
|
(string-equal
|
|
(substring-no-properties (buffer-string)) "any text"))
|
|
;; `buffer-stale--default-function' checks for
|
|
;; `verify-visited-file-modtime'. We must ensure that
|
|
;; it returns nil.
|
|
(auto-revert-mode 1)
|
|
(should auto-revert-mode)
|
|
|
|
;; Remove file while reverting. We simulate this by
|
|
;; modifying `before-revert-hook'.
|
|
(add-hook
|
|
'before-revert-hook
|
|
(lambda ()
|
|
(when auto-revert-debug
|
|
(message "before-revert-hook %s deleted" buffer-file-name))
|
|
(delete-file buffer-file-name))
|
|
nil t)
|
|
|
|
(ert-with-message-capture auto-revert--messages
|
|
(auto-revert-test--write-file "another text" tmpfile)
|
|
(auto-revert--wait-for-revert buf))
|
|
;; Check, that the buffer hasn't been reverted. File
|
|
;; notification should be disabled, falling back to
|
|
;; polling.
|
|
(should
|
|
(string-match-p
|
|
"any text" (substring-no-properties (buffer-string))))
|
|
|
|
;; Once the file has been recreated, the buffer shall be
|
|
;; reverted.
|
|
(kill-local-variable 'before-revert-hook)
|
|
(ert-with-message-capture auto-revert--messages
|
|
(auto-revert-test--write-file "another text" tmpfile)
|
|
(auto-revert--wait-for-revert buf))
|
|
;; Check, that the buffer has been reverted.
|
|
(should
|
|
(string-match-p
|
|
"another text" (substring-no-properties (buffer-string))))
|
|
|
|
;; An empty file shall still be reverted.
|
|
(ert-with-message-capture auto-revert--messages
|
|
(auto-revert-test--write-file "" tmpfile)
|
|
(auto-revert--wait-for-revert buf))
|
|
;; Check, that the buffer has been reverted.
|
|
(should
|
|
(string-equal "" (substring-no-properties (buffer-string))))))
|
|
|
|
;; Exit.
|
|
(ignore-errors
|
|
(with-current-buffer buf (set-buffer-modified-p nil))
|
|
(kill-buffer buf)))))))
|
|
|
|
(auto-revert--deftest-remote auto-revert-test02-auto-revert-deleted-file
|
|
"Check autorevert for a deleted remote file.")
|
|
|
|
(ert-deftest auto-revert-test03-auto-revert-tail-mode ()
|
|
"Check autorevert tail mode."
|
|
(with-auto-revert-test
|
|
;; `auto-revert-buffers' runs every 5". And we must wait, until the
|
|
;; file has been reverted.
|
|
(ert-with-temp-file tmpfile
|
|
:prefix ert-temp-file-prefix
|
|
(let (buf)
|
|
(unwind-protect
|
|
(ert-with-message-capture auto-revert--messages
|
|
(auto-revert-test--write-region "any text" tmpfile)
|
|
(setq buf (find-file-noselect tmpfile))
|
|
(with-current-buffer buf
|
|
;; `buffer-stale--default-function' checks for
|
|
;; `verify-visited-file-modtime'. We must ensure that it
|
|
;; returns nil.
|
|
(auto-revert-tail-mode 1)
|
|
(should auto-revert-tail-mode)
|
|
(erase-buffer)
|
|
(insert "modified text\n")
|
|
(set-buffer-modified-p nil)
|
|
|
|
;; Modify file.
|
|
(auto-revert-test--write-file "another text" tmpfile 'append)
|
|
|
|
;; Check, that the buffer has been reverted.
|
|
(auto-revert--wait-for-revert buf)
|
|
(should
|
|
(string-match-p
|
|
"modified text\nanother text"
|
|
(substring-no-properties (buffer-string))))))
|
|
|
|
;; Exit.
|
|
(ignore-errors
|
|
(with-current-buffer buf (set-buffer-modified-p nil))
|
|
(kill-buffer buf)))))))
|
|
|
|
(auto-revert--deftest-remote auto-revert-test03-auto-revert-tail-mode
|
|
"Check remote autorevert tail mode.")
|
|
|
|
(ert-deftest auto-revert-test04-auto-revert-mode-dired ()
|
|
"Check autorevert for dired."
|
|
;; `auto-revert-buffers' runs every 5". And we must wait, until the
|
|
;; file has been reverted.
|
|
(with-auto-revert-test
|
|
(ert-with-temp-directory tmpdir
|
|
:prefix (concat ert-temp-file-prefix "-parent")
|
|
(ert-with-temp-file tmpfile
|
|
:prefix (expand-file-name "auto-revert-test" tmpdir)
|
|
(let ((name (file-name-nondirectory tmpfile))
|
|
buf)
|
|
(unwind-protect
|
|
(progn
|
|
(setq buf (dired-noselect tmpdir))
|
|
(with-current-buffer buf
|
|
;; `buffer-stale--default-function' checks for
|
|
;; `verify-visited-file-modtime'. We must ensure that
|
|
;; it returns nil.
|
|
(auto-revert-mode 1)
|
|
(should auto-revert-mode)
|
|
(should
|
|
(string-match-p
|
|
(rx bow (literal name) eow)
|
|
(substring-no-properties (buffer-string))))
|
|
;; If we don't sleep for a while, this test fails on
|
|
;; MS-Windows.
|
|
(if (eq system-type 'windows-nt)
|
|
(sleep-for 0.5))
|
|
|
|
;; File stamps of remote files have an accuracy of 1
|
|
;; second. Wait a little bit.
|
|
(when (file-remote-p tmpfile)
|
|
(sleep-for (auto-revert--timeout)))
|
|
(ert-with-message-capture auto-revert--messages
|
|
;; Delete file.
|
|
(delete-file tmpfile)
|
|
(auto-revert--wait-for-revert buf))
|
|
(if (eq system-type 'windows-nt)
|
|
(sleep-for 1))
|
|
;; Check, that the buffer has been reverted.
|
|
(should-not
|
|
(string-match-p
|
|
(rx bow (literal name) eow)
|
|
(substring-no-properties (buffer-string))))
|
|
|
|
;; File stamps of remote files have an accuracy of 1
|
|
;; second. Wait a little bit.
|
|
(when (file-remote-p tmpfile)
|
|
(sleep-for (auto-revert--timeout)))
|
|
(ert-with-message-capture auto-revert--messages
|
|
;; Make dired buffer modified. Check, that the
|
|
;; buffer has been still reverted.
|
|
(set-buffer-modified-p t)
|
|
(auto-revert-test--write-region "any text" tmpfile)
|
|
(auto-revert--wait-for-revert buf))
|
|
;; Check, that the buffer has been reverted.
|
|
(should
|
|
(string-match-p
|
|
(rx bow (literal name) eow)
|
|
(substring-no-properties (buffer-string))))))
|
|
|
|
;; Exit.
|
|
(ignore-errors
|
|
(with-current-buffer buf (set-buffer-modified-p nil))
|
|
(kill-buffer buf))))))))
|
|
|
|
(auto-revert--deftest-remote auto-revert-test04-auto-revert-mode-dired
|
|
"Check remote autorevert for dired.")
|
|
|
|
(defun auto-revert-test--buffer-string (buffer)
|
|
"Contents of BUFFER as a string."
|
|
(with-current-buffer buffer
|
|
(substring-no-properties (buffer-string))))
|
|
|
|
(defun auto-revert-test--wait-for (pred max-wait)
|
|
"Wait until PRED is true, or MAX-WAIT seconds elapsed."
|
|
(let ((ct (current-time)))
|
|
(while (and (< (float-time (time-subtract nil ct)) max-wait)
|
|
(not (funcall pred)))
|
|
(read-event nil nil 0.1))))
|
|
|
|
(defun auto-revert-test--wait-for-buffer-text (buffer string max-wait)
|
|
"Wait until BUFFER has the contents STRING, or MAX-WAIT seconds elapsed."
|
|
(auto-revert-test--wait-for
|
|
(lambda () (string-equal (auto-revert-test--buffer-string buffer) string))
|
|
max-wait))
|
|
|
|
(defun auto-revert-test--instrument-kill-buffer-hook (buffer)
|
|
"Instrument local `kill-buffer-hook' with messages."
|
|
(when auto-revert-debug
|
|
(with-current-buffer buffer
|
|
(add-hook
|
|
'kill-buffer-hook
|
|
(lambda ()
|
|
(message
|
|
"%S killed\n%s" (current-buffer) (with-output-to-string (backtrace))))
|
|
nil 'local))))
|
|
|
|
(ert-deftest auto-revert-test05-global-notify ()
|
|
"Test `global-auto-revert-mode'."
|
|
(skip-unless (or file-notify--library
|
|
(file-remote-p auto-revert--test-rootdir)))
|
|
|
|
(with-auto-revert-test
|
|
(ert-with-temp-file file-1
|
|
:prefix ert-temp-file-prefix
|
|
(ert-with-temp-file file-2
|
|
:prefix ert-temp-file-prefix
|
|
(ert-with-temp-file file-3
|
|
:prefix ert-temp-file-prefix
|
|
(let ((auto-revert-use-notify t)
|
|
(was-in-global-auto-revert-mode global-auto-revert-mode)
|
|
(file-2b (concat file-2 "-b"))
|
|
require-final-newline buf-1 buf-2 buf-3)
|
|
(unwind-protect
|
|
(progn
|
|
(setq buf-1 (find-file-noselect file-1))
|
|
(auto-revert-test--instrument-kill-buffer-hook buf-1)
|
|
(should-not (buffer-local-value 'auto-revert-mode buf-1))
|
|
(should-not (buffer-local-value 'auto-revert--global-mode buf-1))
|
|
(setq buf-2 (find-file-noselect file-2))
|
|
(auto-revert-test--instrument-kill-buffer-hook buf-2)
|
|
(should-not (buffer-local-value 'auto-revert-mode buf-2))
|
|
(should-not (buffer-local-value 'auto-revert--global-mode buf-2))
|
|
;; File stamps of remote files have an accuracy of 1
|
|
;; second. Wait a little bit.
|
|
(when (file-remote-p file-1)
|
|
(sleep-for (auto-revert--timeout)))
|
|
(auto-revert-test--write-file "1-a" file-1)
|
|
(should
|
|
(string-equal (auto-revert-test--buffer-string buf-1) ""))
|
|
|
|
(global-auto-revert-mode 1) ; Turn it on.
|
|
(auto-revert--skip-unless-proper-library-and-monitor buf-1)
|
|
(should-not (buffer-local-value 'auto-revert-mode buf-1))
|
|
(should (buffer-local-value 'auto-revert--global-mode buf-1))
|
|
(should-not (buffer-local-value 'auto-revert-mode buf-2))
|
|
(should (buffer-local-value 'auto-revert--global-mode buf-2))
|
|
|
|
;; buf-1 should have been reverted immediately when the mode
|
|
;; was enabled.
|
|
(should
|
|
(string-equal (auto-revert-test--buffer-string buf-1) "1-a"))
|
|
|
|
;; Alter a file.
|
|
(auto-revert-test--write-region "2-a" file-2)
|
|
(auto-revert-test--wait-for-buffer-text
|
|
buf-2 "2-a" (auto-revert--timeout))
|
|
(should
|
|
(string-equal (auto-revert-test--buffer-string buf-2) "2-a"))
|
|
|
|
;; Visit a file, and modify it on disk.
|
|
(setq buf-3 (find-file-noselect file-3))
|
|
(auto-revert-test--instrument-kill-buffer-hook buf-3)
|
|
(should-not (buffer-local-value 'auto-revert-mode buf-3))
|
|
(should (buffer-local-value 'auto-revert--global-mode buf-3))
|
|
(auto-revert-test--write-region "3-a" file-3)
|
|
(auto-revert-test--wait-for-buffer-text
|
|
buf-3 "3-a" (auto-revert--timeout))
|
|
(should
|
|
(string-equal (auto-revert-test--buffer-string buf-3) "3-a"))
|
|
|
|
;; Delete a visited file, and re-create it with new contents.
|
|
(delete-file file-1)
|
|
(should
|
|
(string-equal (auto-revert-test--buffer-string buf-1) "1-a"))
|
|
(auto-revert-test--write-region "1-b" file-1)
|
|
;; Since the file is deleted, it needs at least
|
|
;; `auto-revert-interval' to recognize the new file,
|
|
;; while polling. So increase the timeout.
|
|
(auto-revert-test--wait-for-buffer-text
|
|
buf-1 "1-b" (* 2 (auto-revert--timeout)))
|
|
|
|
;; Write a buffer to a new file, then modify the new
|
|
;; file on disk.
|
|
(with-current-buffer buf-2
|
|
(write-file file-2b))
|
|
(should
|
|
(string-equal (auto-revert-test--buffer-string buf-2) "2-a"))
|
|
(auto-revert-test--write-region "2-b" file-2b)
|
|
(auto-revert-test--wait-for-buffer-text
|
|
buf-2 "2-b" (auto-revert--timeout)))
|
|
|
|
;; Clean up.
|
|
(unless was-in-global-auto-revert-mode
|
|
(global-auto-revert-mode 0)) ; Turn it off.
|
|
(dolist (buf (list buf-1 buf-2 buf-3))
|
|
(ignore-errors
|
|
(with-current-buffer buf (setq-local kill-buffer-hook nil))
|
|
(kill-buffer buf)))
|
|
(ignore-errors (delete-file file-2b)))))))))
|
|
|
|
(auto-revert--deftest-remote auto-revert-test05-global-notify
|
|
"Test `global-auto-revert-mode' without polling for remote buffers.")
|
|
|
|
(ert-deftest auto-revert-test06-write-file ()
|
|
"Verify that notification follows `write-file' correctly."
|
|
(skip-unless (or file-notify--library
|
|
(file-remote-p auto-revert--test-rootdir)))
|
|
|
|
(with-auto-revert-test
|
|
(ert-with-temp-file file-1
|
|
:prefix ert-temp-file-prefix
|
|
(let ((auto-revert-use-notify t)
|
|
(file-2 (concat file-1 "-2"))
|
|
require-final-newline buf)
|
|
(unwind-protect
|
|
(progn
|
|
(setq buf (find-file-noselect file-1))
|
|
(with-current-buffer buf
|
|
(insert "A")
|
|
(save-buffer)
|
|
|
|
(auto-revert-mode 1)
|
|
(auto-revert--skip-unless-proper-library-and-monitor)
|
|
|
|
(insert "B")
|
|
(write-file file-2)
|
|
|
|
;; File stamps of remote files have an accuracy of 1
|
|
;; second. Wait a little bit.
|
|
(when (file-remote-p file-1)
|
|
(sleep-for (auto-revert--timeout)))
|
|
(auto-revert-test--write-region "C" file-2)
|
|
(auto-revert-test--wait-for-buffer-text
|
|
buf "C" (auto-revert--timeout))
|
|
(should
|
|
(string-equal (substring-no-properties (buffer-string)) "C"))))
|
|
|
|
;; Clean up.
|
|
(ignore-errors
|
|
(with-current-buffer buf (set-buffer-modified-p nil))
|
|
(kill-buffer buf))
|
|
(ignore-errors (delete-file file-2)))))))
|
|
|
|
(auto-revert--deftest-remote auto-revert-test06-write-file
|
|
"Test `write-file' in `auto-revert-mode' for remote buffers.")
|
|
|
|
;; This is inspired by Bug#44638, Bug#71424.
|
|
(ert-deftest auto-revert-test07-auto-revert-several-buffers ()
|
|
"Check autorevert for several buffers visiting the same file."
|
|
(skip-unless (or file-notify--library
|
|
(file-remote-p auto-revert--test-rootdir)))
|
|
|
|
(with-auto-revert-test
|
|
(ert-with-temp-file tmpfile
|
|
:prefix ert-temp-file-prefix
|
|
(let ((auto-revert-use-notify t)
|
|
(num-buffers 10)
|
|
require-final-newline buffers)
|
|
|
|
(unwind-protect
|
|
;; Check indirect buffers.
|
|
(ert-with-message-capture auto-revert--messages
|
|
(auto-revert-test--write-region "any text" tmpfile)
|
|
(push (find-file-noselect tmpfile) buffers)
|
|
(with-current-buffer (car buffers)
|
|
(should
|
|
(string-equal
|
|
(substring-no-properties (buffer-string)) "any text"))
|
|
;; `buffer-stale--default-function' checks for
|
|
;; `verify-visited-file-modtime'. We must ensure that
|
|
;; it returns nil.
|
|
(auto-revert-mode 1)
|
|
(should auto-revert-mode))
|
|
|
|
(dolist (clone '(clone nil))
|
|
(dotimes (i num-buffers)
|
|
(push (make-indirect-buffer
|
|
(car (last buffers))
|
|
(format "%s-%d-%s"
|
|
(buffer-file-name (car (last buffers))) i clone)
|
|
clone)
|
|
buffers)))
|
|
(setq buffers (nreverse buffers))
|
|
(dolist (buf buffers)
|
|
(with-current-buffer buf
|
|
(should
|
|
(string-equal
|
|
(substring-no-properties (buffer-string)) "any text"))
|
|
(if (string-suffix-p "-nil" (buffer-name buf))
|
|
(should-not auto-revert-mode)
|
|
(should auto-revert-mode))))
|
|
|
|
(auto-revert-test--write-file "another text" tmpfile)
|
|
;; Check, that the buffer has been reverted.
|
|
(auto-revert--wait-for-revert (car buffers))
|
|
(dolist (buf buffers)
|
|
(with-current-buffer buf
|
|
(should
|
|
(string-equal
|
|
(substring-no-properties (buffer-string)) "another text"))))
|
|
|
|
;; Disabling autorevert in an indirect buffer does not
|
|
;; disable autorevert in the corresponding base buffer.
|
|
(dolist (buf (cdr buffers))
|
|
(with-current-buffer buf
|
|
(auto-revert-mode 0)
|
|
(should-not auto-revert-mode))
|
|
(with-current-buffer (car buffers)
|
|
(should auto-revert-mode)))
|
|
|
|
;; Killing an indirect buffer does not disable autorevert in
|
|
;; the corresponding base buffer.
|
|
(dolist (buf (cdr buffers))
|
|
(kill-buffer buf))
|
|
(with-current-buffer (car buffers)
|
|
(should auto-revert-mode)))
|
|
|
|
;; Exit.
|
|
(ignore-errors
|
|
(dolist (buf buffers)
|
|
(with-current-buffer buf (set-buffer-modified-p nil))
|
|
(kill-buffer buf)))
|
|
(setq buffers nil)
|
|
(ignore-errors (delete-file tmpfile)))
|
|
|
|
;; Check direct buffers.
|
|
(unwind-protect
|
|
(ert-with-message-capture auto-revert--messages
|
|
(auto-revert-test--write-region "any text" tmpfile)
|
|
|
|
(dotimes (i num-buffers)
|
|
(push (generate-new-buffer
|
|
(format "%s-%d" (file-name-nondirectory tmpfile) i))
|
|
buffers))
|
|
(setq buffers (nreverse buffers))
|
|
(dolist (buf buffers)
|
|
(with-current-buffer buf
|
|
(insert-file-contents tmpfile 'visit)
|
|
(should
|
|
(string-equal
|
|
(substring-no-properties (buffer-string)) "any text"))
|
|
(auto-revert-mode 1)
|
|
(should auto-revert-mode)))
|
|
|
|
(auto-revert-test--write-file "another text" tmpfile)
|
|
;; Check, that the buffers have been reverted.
|
|
(dolist (buf buffers)
|
|
(auto-revert--wait-for-revert buf)
|
|
(with-current-buffer buf
|
|
(should
|
|
(string-equal
|
|
(substring-no-properties (buffer-string)) "another text")))))
|
|
|
|
;; Exit.
|
|
(ignore-errors
|
|
(dolist (buf buffers)
|
|
(with-current-buffer buf (set-buffer-modified-p nil))
|
|
(kill-buffer buf))))))))
|
|
|
|
(auto-revert--deftest-remote auto-revert-test07-auto-revert-several-buffers
|
|
"Check autorevert for several buffers visiting the same remote file.")
|
|
|
|
(ert-deftest auto-revert-test08-auto-revert-inhibit-auto-revert ()
|
|
"Check the power of `inhibit-auto-revert'."
|
|
;; `auto-revert-buffers' runs every 5". And we must wait, until the
|
|
;; file has been reverted.
|
|
(with-auto-revert-test
|
|
(ert-with-temp-file tmpfile
|
|
:prefix ert-temp-file-prefix
|
|
(let (buf)
|
|
(unwind-protect
|
|
(progn
|
|
(auto-revert-test--write-region "any text" tmpfile)
|
|
(setq buf (find-file-noselect tmpfile))
|
|
(with-current-buffer buf
|
|
(ert-with-message-capture auto-revert--messages
|
|
(inhibit-auto-revert
|
|
(auto-revert-mode 1)
|
|
(should auto-revert-mode)
|
|
|
|
(auto-revert-test--write-file "another text" tmpfile)
|
|
;; Check, that the buffer hasn't been reverted.
|
|
(auto-revert--wait-for-revert buf)
|
|
(should-not
|
|
(string-match-p
|
|
"another text" (substring-no-properties (buffer-string)))))
|
|
|
|
;; Check, that the buffer has been reverted.
|
|
(auto-revert--wait-for-revert buf)
|
|
(should
|
|
(string-match-p
|
|
"another text" (substring-no-properties (buffer-string)))))))
|
|
|
|
;; Exit.
|
|
(ignore-errors
|
|
(with-current-buffer buf (set-buffer-modified-p nil))
|
|
(kill-buffer buf)))))))
|
|
|
|
(auto-revert--deftest-remote auto-revert-test08-auto-revert-inhibit-auto-revert
|
|
"Check the power of `inhibit-auto-revert' on a remote file.")
|
|
|
|
;; Mark all tests as unstable on Cygwin (bug#49665).
|
|
(when (eq system-type 'cygwin)
|
|
(dolist (test (apropos-internal "^auto-revert" #'ert-test-boundp))
|
|
(setf (ert-test-tags (ert-get-test test))
|
|
(cons :unstable (ert-test-tags (ert-get-test test))))))
|
|
|
|
(defun auto-revert-test-all (&optional interactive)
|
|
"Run all tests for \\[auto-revert]."
|
|
(interactive "p")
|
|
(if interactive
|
|
(ert-run-tests-interactively "^auto-revert-")
|
|
(ert-run-tests-batch "^auto-revert-")))
|
|
|
|
(provide 'autorevert-tests)
|
|
;;; autorevert-tests.el ends here
|