From: Michael Albinus Date: Sat, 5 Jul 2025 17:21:48 +0000 (+0200) Subject: Add file notification handler for Tramp's "smb" method. X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=c16d9780be4c90edc4dab15e0040640b35208854;p=emacs.git Add file notification handler for Tramp's "smb" method. * etc/NEWS: Mention new file notification handler for Tramp "smb". * lisp/filenotify.el (file-notify--expand-file-name): Fix the remote case. (file-notify-callback): Extend for "smb-notify". (file-notify--call-handler): Fix debug message. * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-file-notify-add-watch): * lisp/net/tramp-sh.el (tramp-sh-gio-monitor-process-filter): Use connection property "file-monitor". * lisp/net/tramp-smb.el (tramp-smb-file-name-handler-alist): Use `tramp-smb-handle-file-notify-add-watch'. (tramp-smb-handle-delete-directory): Do not error if there is a pending deletion of a directory under file-watch. (tramp-smb-handle-file-notify-add-watch) (tramp-smb-notify-process-filter): New defuns. (tramp-smb-send-command): New optional argument NOOUTPUT. (tramp-smb-wait-for-output): Improve debug message. * lisp/net/tramp.el (tramp-directory-watched): New defun. (tramp-accept-process-output, tramp-wait-for-regexp): Improve debug message. * test/lisp/filenotify-tests.el (top): Filter also for "smb-notify". Set some other Tramp related variables. (file-notify--test-wait-for-events) (file-notify--test-with-actions-check) (file-notify--test-with-actions): Add debug message. (file-notify--test-cleanup, file-notify--deftest-remote): Keep Tramp debugs buffer. (file-notify--test-monitor): Check for "smb-notify". (file-notify--deftest-remote): Call `file-notify-rm-all-watches'. (file-notify--test-make-temp-name): Use a better name for parent directory. (file-notify--test-event-handler): Use `string-match-p'. (file-notify--test-with-actions): Check also for the `stopped' event as limit. (file-notify-test03-events, file-notify-test04-autorevert) (file-notify-test05-file-validity) (file-notify-test07-many-events, file-notify-test08-backup) (file-notify-test09-watched-file-in-watched-dir): Adapt tests for "smb-notify". (file-notify-test12-unmount): Skip for "smb-notify". (cherry picked from commit aa8afabd493ce67924685bc6cfafec551380a094) --- diff --git a/lisp/filenotify.el b/lisp/filenotify.el index f31a4353513..300c9babfdb 100644 --- a/lisp/filenotify.el +++ b/lisp/filenotify.el @@ -123,7 +123,9 @@ It is nil or a `file-notify--rename' defstruct where the cookie can be nil.") (defun file-notify--expand-file-name (watch file) "Full file name of FILE reported for WATCH." (directory-file-name - (expand-file-name file (file-notify--watch-directory watch)))) + (if (file-name-absolute-p file) + (concat (file-remote-p (file-notify--watch-directory watch)) file) + (expand-file-name file (file-notify--watch-directory watch))))) (cl-defun file-notify--callback-inotify ((desc actions file &optional file1-or-cookie)) @@ -189,7 +191,7 @@ It is nil or a `file-notify--rename' defstruct where the cookie can be nil.") "Notification callback for file name handlers." (file-notify--handle-event desc - ;; File name handlers use gfilenotify or inotify actions. + ;; File name handlers use gfilenotify, inotify or w32notify actions. (delq nil (mapcar (lambda (action) (cond @@ -205,7 +207,12 @@ It is nil or a `file-notify--rename' defstruct where the cookie can be nil.") ((memq action '(delete delete-self move-self)) 'deleted) ((eq action 'moved-from) 'renamed-from) ((eq action 'moved-to) 'renamed-to) - ((memq action '(ignored unmount)) 'stopped))) + ((memq action '(ignored unmount)) 'stopped) + ;; w32notify actions: + ((eq action 'added) 'created) + ((eq action 'modified) 'changed) + ((eq action 'removed) 'deleted) + ((memq action '(renamed-from renamed-to)) action))) (if (consp actions) actions (list actions)))) file file1-or-cookie)) @@ -237,7 +244,7 @@ It is nil or a `file-notify--rename' defstruct where the cookie can be nil.") (when (file-notify--watch-callback watch) (when file-notify-debug (message - "file-notify-callback %S %S %S %S %S %S %S" + "file-notify--call-handler %S %S %S %S %S %S %S" desc action file file1 watch (file-notify--watch-absolute-filename watch) (file-notify--watch-directory watch))) diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el index fa32d5763cc..7bc8f7ccfbd 100644 --- a/lisp/net/tramp-gvfs.el +++ b/lisp/net/tramp-gvfs.el @@ -1516,6 +1516,7 @@ If FILE-SYSTEM is non-nil, return file system attributes." (if (not (processp p)) (tramp-error v 'file-notify-error "Monitoring not supported for `%s'" file-name) + ;; Needed for process filter. (process-put p 'tramp-events events) (process-put p 'tramp-watch-name localname) (set-process-filter p #'tramp-gvfs-monitor-process-filter) @@ -1527,9 +1528,9 @@ If FILE-SYSTEM is non-nil, return file system attributes." (unless (process-live-p p) (tramp-error p 'file-notify-error "Monitoring not supported for `%s'" file-name)) - ;; Set "gio-file-monitor" property. We believe, that "gio + ;; Set "file-monitor" property. We believe, that "gio ;; monitor" uses polling when applied for mounted files. - (tramp-set-connection-property p "gio-file-monitor" 'GPollFileMonitor) + (tramp-set-connection-property p "file-monitor" 'GPollFileMonitor) p)))) (defun tramp-gvfs-monitor-process-filter (proc string) diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index db098aae53c..41fecbf02c6 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -3893,9 +3893,9 @@ Fall back to normal file name handler if no Tramp handler exists." (throw 'doesnt-work nil)) ;; Determine monitor name. - (unless (tramp-connection-property-p proc "gio-file-monitor") + (unless (tramp-connection-property-p proc "file-monitor") (tramp-set-connection-property - proc "gio-file-monitor" + proc "file-monitor" (cond ;; We have seen this on cygwin gio and on emba. Let's make ;; some assumptions. diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el index e9ee3228a6d..85b25d5ba10 100644 --- a/lisp/net/tramp-smb.el +++ b/lisp/net/tramp-smb.el @@ -127,10 +127,10 @@ this variable \"client min protocol=NT1\"." "ERRnomem" "ERRnosuchshare" ;; See /usr/include/samba-4.0/core/ntstatus.h. - ;; Windows 4.0 (Windows NT), Windows 5.0 (Windows 2000), - ;; Windows 5.1 (Windows XP), Windows 5.2 (Windows Server 2003), - ;; Windows 6.0 (Windows Vista), Windows 6.1 (Windows 7), - ;; Windows 6.3 (Windows Server 2012, Windows 10). + ;; + ;; Tested with Windows NT, Windows 2000, Windows XP, Windows + ;; Server 2003, Windows Vista, Windows 7, Windows Server 2012, + ;; Windows 10, Windows 11. "NT_STATUS_ACCESS_DENIED" "NT_STATUS_ACCOUNT_LOCKED_OUT" "NT_STATUS_BAD_NETWORK_NAME" @@ -261,7 +261,7 @@ See `tramp-actions-before-shell' for more info.") (file-name-nondirectory . tramp-handle-file-name-nondirectory) ;; `file-name-sans-versions' performed by default handler. (file-newer-than-file-p . tramp-handle-file-newer-than-file-p) - (file-notify-add-watch . tramp-handle-file-notify-add-watch) + (file-notify-add-watch . tramp-smb-handle-file-notify-add-watch) (file-notify-rm-watch . tramp-handle-file-notify-rm-watch) (file-notify-valid-p . tramp-handle-file-notify-valid-p) (file-ownership-preserved-p . ignore) @@ -686,8 +686,10 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (tramp-error v 'file-error "%s `%s'" (match-string 0) directory))) ;; "rmdir" does not report an error. So we check ourselves. - (when (file-exists-p directory) - (tramp-error v 'file-error "`%s' not removed" directory))))) + ;; Deletion of a watched directory could be pending. + (when (and (not (tramp-directory-watched directory)) + (file-exists-p directory)) + (tramp-error v 'file-error "`%s' not removed" directory))))) (defun tramp-smb-handle-delete-file (filename &optional trash) "Like `delete-file' for Tramp files." @@ -964,6 +966,108 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (tramp-error v 'file-error "Cannot make local copy of file `%s'" filename))))) +;; The "notify" command has been added to smbclient 4.3.0. +(defun tramp-smb-handle-file-notify-add-watch (file-name flags _callback) + "Like `file-notify-add-watch' for Tramp files." + (setq file-name (expand-file-name file-name)) + (with-parsed-tramp-file-name file-name nil + (let ((default-directory (file-name-directory file-name)) + (command (format "notify %s" (tramp-smb-shell-quote-localname v))) + (events + (cond + ((memq 'change flags) + '(added removed modified renamed-from renamed-to)) + ((memq 'attribute-change flags) '(modified)))) + p) + ;; Start process. + (with-tramp-saved-connection-properties + v '(" process-name" " process-buffer") + ;; Set the new process properties. + (tramp-set-connection-property + v " process-name" (tramp-get-unique-process-name "smb-notify")) + (tramp-set-connection-property + v " process-buffer" (generate-new-buffer " *smb-notify*")) + (tramp-flush-connection-property v " process-exit-status") + (tramp-smb-send-command v command 'nooutput) + (setq p (tramp-get-connection-process v)) + ;; Return the process object as watch-descriptor. + (if (not (processp p)) + (tramp-error + v 'file-notify-error + "`%s' failed to start on remote host" command) + ;; Needed for process filter. + (process-put p 'tramp-events events) + (process-put p 'tramp-watch-name localname) + (set-process-filter p #'tramp-smb-notify-process-filter) + (set-process-sentinel p #'tramp-file-notify-process-sentinel) + (tramp-post-process-creation p v) + ;; There might be an error if the monitor is not supported. + ;; Give the filter a chance to read the output. + (while (tramp-accept-process-output p)) + (unless (process-live-p p) + (tramp-error + p 'file-notify-error "Monitoring not supported for `%s'" file-name)) + ;; Set "file-monitor" property. The existence of the "ADMIN$" + ;; share is an indication for a remote MS Windows host. + (tramp-set-connection-property + p "file-monitor" + (if (member + "ADMIN$" (directory-files (tramp-make-tramp-file-name v "/"))) + 'SMBWindows 'SMBSamba)) + p))))) + +;; FileChangeNotify subsystem was added to Smaba 4.3.0. +;; +(defun tramp-smb-notify-process-filter (proc string) + "Read output from \"notify\" and add corresponding `file-notify' events." + (let ((events (process-get proc 'tramp-events))) + (tramp-message proc 6 "%S\n%s" proc string) + (dolist (line (split-string string (rx (+ (any "\r\n"))) 'omit)) + (catch 'next + ;; Watched directory is removed. + (when (string-match-p "NT_STATUS_DELETE_PENDING" line) + (setq line (concat "0002 " (process-get proc 'tramp-watch-name)))) + ;; Stopped. + (when (string-match-p tramp-smb-prompt line) + (throw 'next 'next)) + + ;; Check, whether there is a problem. + (unless (string-match + (rx bol (group (+ digit)) + (+ blank) (group (+ (not (any "\r\n"))))) + line) + (tramp-error proc 'file-notify-error line)) + + ;; See libsmbclient.h. + ;; #define SMBC_NOTIFY_ACTION_ADDED 1 + ;; #define SMBC_NOTIFY_ACTION_REMOVED 2 + ;; #define SMBC_NOTIFY_ACTION_MODIFIED 3 + ;; #define SMBC_NOTIFY_ACTION_OLD_NAME 4 + ;; #define SMBC_NOTIFY_ACTION_NEW_NAME 5 + ;; #define SMBC_NOTIFY_ACTION_ADDED_STREAM 6 + ;; #define SMBC_NOTIFY_ACTION_REMOVED_STREAM 7 + ;; #define SMBC_NOTIFY_ACTION_MODIFIED_STREAM 8 + (let ((object + (list + proc + (pcase (string-to-number (match-string 1 line)) + (1 '(added)) + (2 '(removed)) + (3 '(modified)) + (4 '(renamed-from)) + (5 '(renamed-to)) + ;; Ignore stream events. + (_ (throw 'next 'next))) + (string-replace "\\" "/" (match-string 2 line))))) + ;; Add an Emacs event now. + ;; `insert-special-event' exists since Emacs 31. + (when (member (caadr object) events) + (tramp-compat-funcall + (if (fboundp 'insert-special-event) + 'insert-special-event + (lookup-key special-event-map [file-notify])) + `(file-notify ,object file-notify-callback)))))))) + ;; This function should return "foo/" for directories and "bar" for ;; files. (defun tramp-smb-handle-file-name-all-completions (filename directory) @@ -1823,13 +1927,14 @@ are listed. Result is the list (LOCALNAME MODE SIZE MTIME)." ;; Connection functions. -(defun tramp-smb-send-command (vec command) +(defun tramp-smb-send-command (vec command &optional nooutput) "Send the COMMAND to connection VEC. -Returns nil if there has been an error message from smbclient." +Returns nil if there has been an error message from smbclient. The +function waits for output unless NOOUTPUT is set." (tramp-smb-maybe-open-connection vec) (tramp-message vec 6 "%s" command) (tramp-send-string vec command) - (tramp-smb-wait-for-output vec)) + (unless nooutput (tramp-smb-wait-for-output vec))) (defun tramp-smb-maybe-open-connection (vec &optional argument) "Maybe open a connection to HOST, log in as USER, using `tramp-smb-program'. @@ -2003,7 +2108,7 @@ Removes smb prompt. Returns nil if an error message has appeared." (while (not (search-forward-regexp tramp-smb-prompt nil t)) (while (tramp-accept-process-output p)) (goto-char (point-min))) - (tramp-message vec 6 "\n%s" (buffer-string)) + (tramp-message vec 6 "%S\n%s" p (buffer-string)) ;; Remove prompt. (goto-char (point-min)) @@ -2084,4 +2189,6 @@ Removes smb prompt. Returns nil if an error message has appeared." ;; ;; * Keep a permanent connection process for `process-file'. +;; * Implement "scopy" (since Samba 4.3.0). + ;;; tramp-smb.el ends here diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index 0d7b157c857..d64f6ba37ac 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -5743,6 +5743,16 @@ of." (tramp-message proc 5 "Sentinel called: `%S' `%s'" proc event) (file-notify-rm-watch proc))) +(defun tramp-directory-watched (directory) + "Check, whether a directory is watched." + (let (result) + (dolist (p (process-list) result) + (setq result + (or result + (and-let* ((dir (process-get p 'tramp-watch-name)) + ((string-equal + dir (tramp-file-local-name directory)))))))))) + ;;; Functions for establishing connection: ;; The following functions are actions to be taken when seeing certain @@ -6120,7 +6130,7 @@ If the user quits via `C-g', it is propagated up to `tramp-file-name-handler'." (if (with-local-quit (setq result (accept-process-output proc 0 nil t)) t) (tramp-message - proc 10 "%s %s %s\n%s" + proc 10 "%S %S %s\n%s" proc (process-status proc) result (buffer-string)) ;; Propagate quit. (keyboard-quit))) @@ -6197,7 +6207,7 @@ nil." ;; timeout of sudo. The process buffer does not exist any longer then. (ignore-errors (tramp-message - proc 6 "\n%s" (tramp-get-buffer-string (process-buffer proc)))) + proc 6 "%S\n%s" proc (tramp-get-buffer-string (process-buffer proc)))) (unless found (if timeout (tramp-error diff --git a/test/lisp/filenotify-tests.el b/test/lisp/filenotify-tests.el index 8744dc4987a..dada26fc618 100644 --- a/test/lisp/filenotify-tests.el +++ b/test/lisp/filenotify-tests.el @@ -37,7 +37,7 @@ ;; 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-monitor" and "gvfs-monitor-dir". +;; "inotifywait", "gio-monitor", "gvfs-monitor-dir", and "smb-notify". ;; Local file-notify libraries are auto-detected during Emacs ;; configuration. This can be changed with a respective configuration @@ -58,7 +58,7 @@ ;; Filter suppressed remote file-notify libraries. (when (stringp (getenv "REMOTE_FILE_NOTIFY_LIBRARY")) - (dolist (lib '("inotifywait" "gio-monitor" "gvfs-monitor-dir")) + (dolist (lib '("inotifywait" "gio-monitor" "gvfs-monitor-dir" "smb-notify")) (unless (string-equal (getenv "REMOTE_FILE_NOTIFY_LIBRARY") lib) (add-to-list 'tramp-connection-properties `(nil ,lib nil))))) @@ -104,6 +104,9 @@ There are different timeouts for local and remote file notification libraries." TIMEOUT is the maximum time to wait for, in seconds." `(with-timeout (,timeout (ignore)) (while (null ,until) + (when file-notify-debug + (message "file-notify--test-wait-for-events received: %s" + (file-notify--test-event-actions))) (file-notify--test-wait-event)))) (defun file-notify--test-no-descriptors () @@ -159,7 +162,7 @@ Return nil when any other file notification watch is still active." (ignore-errors (when (file-remote-p temporary-file-directory) (tramp-cleanup-connection - (tramp-dissect-file-name temporary-file-directory) nil 'keep-password))) + (tramp-dissect-file-name temporary-file-directory) t 'keep-password))) (when (hash-table-p file-notify-descriptors) (clrhash file-notify-descriptors)) @@ -176,9 +179,13 @@ Return nil when any other file notification watch is still active." file-notify--test-events nil file-notify--test-monitors nil)) -(setq file-notify-debug nil +(setq auth-source-cache-expiry nil + auth-source-save-behavior nil + file-notify-debug nil password-cache-expiry nil - ;; tramp-verbose (if (getenv "EMACS_EMBA_CI") 10 0) + 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 @@ -241,13 +248,17 @@ 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) and GPollFileMonitor (gio on cygwin). + ;; - 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 file-notify--test-desc (or (alist-get file-notify--test-desc file-notify--test-monitors) - (when (member (file-notify--test-library) '("gfilenotify" "gio")) + (when (member + (file-notify--test-library) '("gfilenotify" "gio" "smb-notify")) (add-to-list 'file-notify--test-monitors (cons file-notify--test-desc @@ -255,10 +266,10 @@ watch descriptor." ;; `file-notify--test-desc' is the connection process. (progn (while (not (tramp-connection-property-p - file-notify--test-desc "gio-file-monitor")) + file-notify--test-desc "file-monitor")) (accept-process-output file-notify--test-desc 0)) (tramp-get-connection-property - file-notify--test-desc "gio-file-monitor" nil)) + file-notify--test-desc "file-monitor" nil)) (and (functionp 'gfile-monitor-name) (gfile-monitor-name file-notify--test-desc))))) ;; If we don't know the monitor, there are good chances the @@ -282,7 +293,8 @@ If UNSTABLE is non-nil, the test is tagged as `:unstable'." ;; Needs further investigation. (skip-when (string-equal (file-notify--test-library) "gio")) (tramp-cleanup-connection - (tramp-dissect-file-name temporary-file-directory) nil 'keep-password) + (tramp-dissect-file-name temporary-file-directory) t 'keep-password) + (file-notify-rm-all-watches) (funcall (ert-test-body ert-test))))) (ert-deftest file-notify-test00-availability () @@ -315,7 +327,7 @@ If UNSTABLE is non-nil, the test is tagged as `:unstable'." (unless (stringp file-notify--test-tmpdir) (setq file-notify--test-tmpdir (expand-file-name - (make-temp-name "file-notify-test") temporary-file-directory))) + (make-temp-name "file-notify-test-parent") temporary-file-directory))) (unless (file-directory-p file-notify--test-tmpdir) (make-directory file-notify--test-tmpdir)) (expand-file-name @@ -558,7 +570,7 @@ and the event to `file-notify--test-events'." (result (ert-run-test (make-ert-test :body 'file-notify--test-event-test)))) ;; Do not add lock files, this would confuse the checks. - (unless (string-match + (unless (string-match-p (regexp-quote ".#") (file-notify--test-event-file file-notify--test-event)) (when file-notify-debug @@ -575,6 +587,8 @@ and the event to `file-notify--test-events'." (defun file-notify--test-with-actions-check (actions) "Check whether received actions match one of the ACTIONS alternatives." + (when file-notify-debug + (message "file-notify--test-with-actions-check")) (let (result) (dolist (elt actions result) (setq result @@ -632,11 +646,14 @@ delivered." (not (input-pending-p))) (setq file-notify--test-events nil file-notify--test-results nil) + (when file-notify-debug + (message "file-notify--test-with-actions expected: %s" actions)) ,@body (file-notify--test-wait-for-events ;; More actions need more time. Use some fudge factor. (* (ceiling max-length 100) (file-notify--test-timeout)) - (= max-length (length file-notify--test-events))) + (or (= max-length (length file-notify--test-events)) + (memq 'stopped (file-notify--test-event-actions)))) ;; Check the result sequence just to make sure that all actions ;; are as expected. (dolist (result file-notify--test-results) @@ -666,6 +683,9 @@ delivered." '(change) #'file-notify--test-event-handler))) (file-notify--test-with-actions (cond + ;; SMBSamba reports three `changed' events. + ((eq (file-notify--test-monitor) 'SMBSamba) + '(created changed changed changed deleted stopped)) ;; GFam{File,Directory}Monitor, GKqueueFileMonitor and ;; GPollFileMonitor do not report the `changed' event. ((memq (file-notify--test-monitor) @@ -697,6 +717,9 @@ delivered." '(change) #'file-notify--test-event-handler))) (file-notify--test-with-actions (cond + ;; SMBSamba reports four `changed' events. + ((eq (file-notify--test-monitor) 'SMBSamba) + '(changed changed changed changed deleted stopped)) ;; GFam{File,Directory}Monitor and GPollFileMonitor do ;; not detect the `changed' event reliably. ((memq (file-notify--test-monitor) @@ -739,6 +762,9 @@ delivered." ;; events for the watched directory. ((string-equal (file-notify--test-library) "w32notify") '(created changed deleted)) + ;; SMBSamba reports three `changed' events. + ((eq (file-notify--test-monitor) 'SMBSamba) + '(created changed changed changed deleted deleted stopped)) ;; On emba, `deleted' and `stopped' events of the ;; directory are not detected. ((getenv "EMACS_EMBA_CI") @@ -789,6 +815,10 @@ delivered." '(created changed created changed changed changed changed deleted deleted)) + ;; SMBSamba reports three `changed' events. + ((eq (file-notify--test-monitor) 'SMBSamba) + '(created changed changed changed created changed changed changed + deleted deleted deleted stopped)) ;; There are three `deleted' events, for two files and ;; for the directory. Except for ;; GFam{File,Directory}Monitor, GPollFileMonitor and @@ -843,6 +873,10 @@ delivered." ;; events for the watched directory. ((string-equal (file-notify--test-library) "w32notify") '(created changed renamed deleted)) + ;; SMBSamba reports three `changed' events. + ((eq (file-notify--test-monitor) 'SMBSamba) + '(created changed changed changed + renamed changed changed deleted deleted stopped)) ;; On emba, `deleted' and `stopped' events of the ;; directory are not detected. ((getenv "EMACS_EMBA_CI") @@ -897,6 +931,14 @@ delivered." ((string-equal (file-notify--test-library) "w32notify") '((changed changed) (changed changed changed changed))) + ;; SMBWindows does not distinguish between `changed' and + ;; `attribute-changed'. + ((eq (file-notify--test-monitor) 'SMBWindows) + '(changed changed)) + ;; SMBSamba does not distinguish between `changed' and + ;; `attribute-changed'. + ((eq (file-notify--test-monitor) 'SMBSamba) + '(changed changed changed changed)) ;; GFam{File,Directory}Monitor, GKqueueFileMonitor and ;; GPollFileMonitor do not report the `attribute-changed' ;; event. @@ -948,6 +990,7 @@ delivered." (timeout (if (file-remote-p temporary-file-directory) 60 ; FIXME: can this be shortened? (* auto-revert-interval 2.5))) + (text-quoting-style 'grave) buf) (auto-revert-set-timer) (unwind-protect @@ -995,10 +1038,11 @@ delivered." ;; Check, that the buffer has been reverted. (file-notify--test-wait-for-events timeout - (string-match - (format-message "Reverting buffer `%s'." (buffer-name buf)) + (string-match-p + (rx bol "Reverting buffer `" + (literal (buffer-name buf)) "'" eol) captured-messages)) - (should (string-match "another text" (buffer-string))))) + (should (string-match-p "another text" (buffer-string))))) ;; Stop file notification. Autorevert shall still work via polling. (file-notify-rm-watch auto-revert-notify-watch-descriptor) @@ -1020,10 +1064,11 @@ delivered." ;; Check, that the buffer has been reverted. (file-notify--test-wait-for-events timeout - (string-match - (format-message "Reverting buffer `%s'." (buffer-name buf)) + (string-match-p + (rx bol "Reverting buffer `" + (literal (buffer-name buf)) "'" eol) captured-messages)) - (should (string-match "foo bla" (buffer-string))))) + (should (string-match-p "foo bla" (buffer-string))))) ;; Stop autorevert, in order to cleanup descriptor. (auto-revert-mode -1)) @@ -1077,6 +1122,9 @@ delivered." (should (file-notify-valid-p file-notify--test-desc)) (file-notify--test-with-actions (cond + ;; SMBSamba reports three `changed' events. + ((eq (file-notify--test-monitor) 'SMBSamba) + '(changed changed changed changed deleted stopped)) ;; GFam{File,Directory}Monitor do not ;; detect the `changed' event reliably. ((memq (file-notify--test-monitor) @@ -1093,6 +1141,7 @@ delivered." "another text" nil file-notify--test-tmpfile nil 'no-message) (file-notify--test-wait-event) (delete-file file-notify--test-tmpfile)) + (file-notify--test-wait-event) ;; After deleting the file, the descriptor is not valid anymore. (should-not (file-notify-valid-p file-notify--test-desc)) (file-notify-rm-watch file-notify--test-desc) @@ -1122,6 +1171,9 @@ delivered." ;; events for the watched directory. ((string-equal (file-notify--test-library) "w32notify") '(created changed deleted)) + ;; SMBSamba reports three `changed' events. + ((eq (file-notify--test-monitor) 'SMBSamba) + '(created changed changed changed deleted deleted stopped)) ;; There are two `deleted' events, for the file and for ;; the directory. Except for ;; GFam{File,Directory}Monitor, GPollFileMonitor and @@ -1247,7 +1299,14 @@ delivered." (push (expand-file-name (format "y%d" i)) target-file-list)) (push (expand-file-name (format "y%d" i)) source-file-list) (push (expand-file-name (format "x%d" i)) target-file-list))) - (file-notify--test-with-actions (make-list (+ n n) 'created) + (file-notify--test-with-actions + (cond + ;; SMBSamba fires both `created' and `changed' events. + ((eq (file-notify--test-monitor) 'SMBSamba) + (let (r) + (dotimes (_i (+ n n) r) + (setq r (append '(created changed) r))))) + (t (make-list (+ n n) 'created))) (let ((source-file-list source-file-list) (target-file-list target-file-list)) (while (and source-file-list target-file-list) @@ -1260,18 +1319,26 @@ delivered." ;; w32notify fires both `deleted' and `renamed' events. ((string-equal (file-notify--test-library) "w32notify") (let (r) - (dotimes (_i n) - (setq r (append '(deleted renamed) r))) - r)) - ;; GFam{File,Directory}Monitor and GPollFileMonitor fire + (dotimes (_i n r) + (setq r (append '(deleted renamed) r))))) + ;; SMBWindows fires both `changed' and `deleted' events. + ((eq (file-notify--test-monitor) 'SMBWindows) + (let (r) + (dotimes (_i n r) + (setq r (append '(changed deleted) r))))) + ;; SMBSamba fires both `changed' and `deleted' events. + ((eq (file-notify--test-monitor) 'SMBSamba) + (let (r) + (dotimes (_i n r) + (setq r (append '(changed changed deleted) r))))) + ;; GFam{File,Directory}Monitor and GPollFileMonitor fire ;; `changed' and `deleted' events, sometimes in random ;; order. ((memq (file-notify--test-monitor) '(GFamFileMonitor GFamDirectoryMonitor GPollFileMonitor)) (let (r) - (dotimes (_i n) - (setq r (append '(changed deleted) r))) - (cons :random r))) + (dotimes (_i n (cons :random r)) + (setq r (append '(changed deleted) r))))) (t (make-list n 'renamed))) (let ((source-file-list source-file-list) (target-file-list target-file-list)) @@ -1315,6 +1382,9 @@ delivered." (should (file-notify-valid-p file-notify--test-desc)) (file-notify--test-with-actions (cond + ;; SMBSamba reports four `changed' events. + ((eq (file-notify--test-monitor) 'SMBSamba) + '(changed changed changed changed)) ;; GKqueueFileMonitor does not report the `changed' event. ((eq (file-notify--test-monitor) 'GKqueueFileMonitor) '()) ;; There could be one or two `changed' events. @@ -1354,6 +1424,12 @@ delivered." (should (file-notify-valid-p file-notify--test-desc)) (file-notify--test-with-actions (cond + ;; SMBWindows reports two `changed' events. + ((eq (file-notify--test-monitor) 'SMBWindows) + '(changed changed)) + ;; SMBSamba reports four `changed' events. + ((eq (file-notify--test-monitor) 'SMBSamba) + '(changed changed changed changed)) ;; GFam{File,Directory}Monitor and GPollFileMonitor ;; report only the `changed' event. ((memq (file-notify--test-monitor) @@ -1438,7 +1514,27 @@ the file watch." (file-notify--test-with-actions ;; There could be one or two `changed' events. (list - ;; cygwin. + ;; SMBSamba. Sometimes, tha last `changed' event is + ;; missing, so we add two alternatives. + (append + '(:random) + ;; Just the file monitor. + (make-list (* (/ n 2) 5) 'changed) + ;; Just the directory monitor. Strange, not all + ;; `changed' events do arrive. + (make-list (1- (* (/ n 2) 10)) 'changed) + (make-list (/ n 2) 'created) + (make-list (/ n 2) 'created)) + (append + '(:random) + ;; Just the file monitor. + (make-list (* (/ n 2) 5) 'changed) + ;; Just the directory monitor. This is the alternative + ;; with all `changed' events. + (make-list (* (/ n 2) 10) 'changed) + (make-list (/ n 2) 'created) + (make-list (/ n 2) 'created)) + ;; cygwin. (append '(:random) (make-list (/ n 2) 'changed) @@ -1482,7 +1578,9 @@ the file watch." ;; directory and the file monitor. The `stopped' event is ;; from the file monitor. It's undecided in which order the ;; directory and the file monitor are triggered. - (file-notify--test-with-actions '(:random deleted deleted stopped) + (file-notify--test-with-actions + '((:random deleted deleted stopped) + (:random deleted deleted deleted stopped)) (delete-file file-notify--test-tmpfile1)) (should (file-notify-valid-p file-notify--test-desc1)) (should-not (file-notify-valid-p file-notify--test-desc2)) @@ -1715,8 +1813,8 @@ the file watch." "Check that file notification stop after unmounting the filesystem." :tags '(:expensive-test) (skip-unless (file-notify--test-local-enabled)) - ;; This test does not work for w32notify. - (skip-when (string-equal (file-notify--test-library) "w32notify")) + ;; This test does not work for w32notify snd smb-notify. + (skip-when (member (file-notify--test-library) '("w32notify" "smb-notify"))) (unwind-protect (progn @@ -1789,8 +1887,8 @@ the file watch." ;; the missing directory monitor. ;; * For w32notify, no `deleted' and `stopped' events arrive when a ;; directory is removed. -;; * For cygwin and w32notify, no `attribute-changed' events arrive. -;; They send `changed' events instead. +;; * For cygwin, w32notify, and smb-notify, no `attribute-changed' +;; events arrive. They send `changed' events instead. ;; * cygwin does not send all expected `changed' and `deleted' events. ;; Probably due to timing issues.