From 86531ac5d93f3cfbb124066b6537c464eb1bc011 Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Mon, 2 Jun 2025 11:44:43 +0200 Subject: [PATCH] Tramp: Do not raise an error when not connected (Bug#78572) * lisp/net/tramp-cmds.el (tramp-cleanup-connection): Use read syntax #' for `tramp-timeout-session', * lisp/net/tramp.el (tramp-barf-if-file-missing): Do not raise an error when not connected. (Bug#78572) (tramp-file-name-handler): Do not force the backtrace. (tramp-connectable-p): Check also, whether initial handshake is finished. (tramp-skeleton-directory-files) (tramp-skeleton-directory-files-and-attributes) (tramp-skeleton-set-file-modes-times-uid-gid): Rearrange sending `file-missing' error. (tramp-handle-access-file, tramp-handle-unlock-file): Use `tramp-connectable-p'. * test/lisp/net/tramp-tests.el (project-mode-line-format) (project-mode-line): Declare. (tramp-test48-session-timeout): New test. (tramp-test49-auto-load, tramp-test49-delay-load) (tramp-test49-recursive-load, tramp-test49-remote-load-path) (tramp-test50-without-remote-files, tramp-test51-unload): Rename. (cherry picked from commit 55691c61d4af972b999bb97bffb46cb8571c912c) --- lisp/net/tramp-cmds.el | 2 +- lisp/net/tramp-sh.el | 1 + lisp/net/tramp.el | 301 ++++++++++++++++++----------------- test/lisp/net/tramp-tests.el | 58 ++++++- 4 files changed, 206 insertions(+), 156 deletions(-) diff --git a/lisp/net/tramp-cmds.el b/lisp/net/tramp-cmds.el index 5afe31cb5e6..88345fa7e16 100644 --- a/lisp/net/tramp-cmds.el +++ b/lisp/net/tramp-cmds.el @@ -175,7 +175,7 @@ interactively, a Tramp connection has to be selected." ;; Cancel timer. (dolist (timer timer-list) - (when (and (eq (timer--function timer) 'tramp-timeout-session) + (when (and (eq (timer--function timer) #'tramp-timeout-session) (tramp-file-name-equal-p vec (car (timer--args timer)))) (cancel-timer timer))) diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index 592a09d2e4e..7ef6cb3de95 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -5169,6 +5169,7 @@ If there is just some editing, retry it after 5 seconds." Does not do anything if a connection is already open, but re-opens the connection if a previous connection has died for some reason." ;; During completion, don't reopen a new connection. + ;; Same for slide-in timer or process-{filter,sentinel}. (unless (tramp-connectable-p vec) (throw 'non-essential 'non-essential)) diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index 2ad6d916d71..0f7b945f84a 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -1349,6 +1349,9 @@ like \"/sys\" or \"/C:\".") ;; buffer local variable, which is computed depending on remote host properties ;; when `tramp-chunksize' is zero or nil. (defcustom tramp-chunksize (when (memq system-type '(hpux)) 500) +;; Parentheses in docstring starting at beginning of line are escaped. +;; Fontification is messed up when +;; `open-paren-in-column-0-is-defun-start' set to t. "If non-nil, chunksize for sending input to local process. It is necessary only on systems which have a buggy `process-send-string' implementation. The necessity, whether this variable must be set, can be @@ -1657,9 +1660,9 @@ entry does not exist, return DEFAULT." ;; We use the cached property. (tramp-get-connection-property vec hash-entry) ;; Use the static value from `tramp-methods'. - (if-let ((methods-entry - (assoc - param (assoc (tramp-file-name-method vec) tramp-methods)))) + (if-let* ((methods-entry + (assoc + param (assoc (tramp-file-name-method vec) tramp-methods)))) (cadr methods-entry) ;; Return the default value. default)))) @@ -1839,13 +1842,7 @@ default values are used." :port port :localname localname :hop hop)) ;; The method must be known. (unless (or nodefault non-essential - (assoc method tramp-methods) - (when-let ((params-fun - (intern-soft - (format "tramp-%s-method-params" method))) - ((functionp params-fun)) - (params (funcall params-fun))) - (push (cons method params) tramp-methods))) + (assoc method tramp-methods)) (tramp-user-error v "Method `%s' is not known" method)) ;; Only some methods from tramp-sh.el do support multi-hops. @@ -2108,10 +2105,11 @@ does not exist, otherwise propagate the error." (declare (indent 2) (debug (symbolp form body))) (let ((err (make-symbol "err"))) `(condition-case ,err - (progn ,@body) + (let (signal-hook-function) ,@body) (error (if (not (or (file-exists-p ,filename) (file-symlink-p ,filename))) - (tramp-error ,vec 'file-missing ,filename) + (when (tramp-connectable-p ,vec) + (tramp-error ,vec 'file-missing ,filename)) (signal (car ,err) (cdr ,err))))))) ;; This function provides traces in case of errors not triggered by @@ -2184,9 +2182,9 @@ without a visible progress reporter." ;; We start a pulsing progress reporter after 3 seconds. ;; Start only when there is no other progress reporter ;; running, and when there is a minimum level. - (when-let ((pr (and (null tramp-inhibit-progress-reporter) - (<= ,level (min tramp-verbose 3)) - (make-progress-reporter ,message)))) + (when-let* ((pr (and (null tramp-inhibit-progress-reporter) + (<= ,level (min tramp-verbose 3)) + (make-progress-reporter ,message)))) (run-at-time 3 0.1 #'tramp-progress-reporter-update pr)))) (unwind-protect ;; Execute the body. @@ -2208,8 +2206,8 @@ without a visible progress reporter." (let ((seconds (car list)) (timeout-forms (cdr list))) ;; If non-nil, `seconds' must be a positive number. - `(if-let (((natnump ,seconds)) - ((not (zerop timeout)))) + `(if-let* (((natnump ,seconds)) + ((not (zerop timeout)))) (with-timeout (,seconds ,@timeout-forms) ,@body) ,@body))) @@ -2431,7 +2429,6 @@ Must be handled by the callers." '(add-name-to-file copy-directory copy-file file-equal-p file-in-directory-p file-name-all-completions file-name-completion - file-name-completion-annotation file-newer-than-file-p rename-file)) (cond ((tramp-tramp-file-p (nth 0 args)) (nth 0 args)) @@ -2565,7 +2562,7 @@ Fall back to normal file name handler if no Tramp file name handler exists." (tramp-message v 5 "Non-essential received in operation %s" (cons operation args)) - (let ((tramp-verbose 10)) (tramp-backtrace v)) + (tramp-backtrace v) (tramp-run-real-handler operation args)) ((eq result 'suppress) (let ((inhibit-message t)) @@ -2597,7 +2594,7 @@ Fall back to normal file name handler if no Tramp file name handler exists." (defun tramp-completion-file-name-handler (operation &rest args) "Invoke Tramp file name completion handler for OPERATION and ARGS. Falls back to normal file name handler if no Tramp file name handler exists." - (if-let + (if-let* ((fn (and tramp-mode minibuffer-completing-file-name (assoc operation tramp-completion-file-name-handler-alist)))) (save-match-data (apply (cdr fn) args)) @@ -2693,7 +2690,7 @@ remote file names." ;; If jka-compr or epa-file are already loaded, move them to the ;; front of `file-name-handler-alist'. (dolist (fnh '(epa-file-handler jka-compr-handler)) - (when-let ((entry (rassoc fnh file-name-handler-alist))) + (when-let* ((entry (rassoc fnh file-name-handler-alist))) (setq file-name-handler-alist (cons entry (delete entry file-name-handler-alist)))))) @@ -2797,13 +2794,15 @@ They are completed by `M-x TAB' only if there's an active connection or buffer." "Check if it is possible to connect the remote host without side-effects. This is true, if either the remote host is already connected, or if we are not in completion mode." - (let ((tramp-verbose 0) - (vec (tramp-ensure-dissected-file-name vec-or-filename))) - (or ;; We check this for the process related to - ;; `tramp-buffer-name'; otherwise `make-process' wouldn't run - ;; ever when `non-essential' is non-nil. - (process-live-p (tramp-get-process vec)) - (not non-essential)))) + (or (not non-essential) + ;; We check this for the process related to `tramp-buffer-name'; + ;; otherwise `make-process' wouldn't run ever when + ;; `non-essential' is non-nil. + (and-let* ((tramp-verbose 0) + (vec (tramp-ensure-dissected-file-name vec-or-filename)) + (p (tramp-get-process vec)) + ((process-live-p p)) + ((tramp-get-connection-property p "connected")))))) (defun tramp-completion-handle-expand-file-name (filename &optional directory) "Like `expand-file-name' for partial Tramp files." @@ -2909,7 +2908,16 @@ BODY is the backend specific code." (dolist (elt '(".." ".")) (when (string-prefix-p ,filename elt) (setq result (cons (concat elt "/") result))))) - result))))) + (if (consp completion-regexp-list) + ;; Discriminate over `completion-regexp-list'. + (mapcar + (lambda (x) + (when (stringp x) + (catch 'match + (dolist (elt completion-regexp-list x) + (unless (string-match-p elt x) (throw 'match nil)))))) + result) + result)))))) (defvar tramp--last-hop-directory nil "Tracks the directory from which to run login programs.") @@ -3465,79 +3473,69 @@ BODY is the backend specific code." "Skeleton for `tramp-*-handle-directory-files'. BODY is the backend specific code." (declare (indent 5) (debug t)) - `(or - (with-parsed-tramp-file-name (expand-file-name ,directory) nil - (tramp-barf-if-file-missing v ,directory - (when (file-directory-p ,directory) - (setf ,directory - (file-name-as-directory (expand-file-name ,directory))) - (let ((temp - (with-tramp-file-property v localname "directory-files" ,@body)) - result item) - (while temp - (setq item (directory-file-name (pop temp))) - (when (or (null ,match) (string-match-p ,match item)) - (push (if ,full (concat ,directory item) item) - result))) - (unless ,nosort - (setq result (sort result #'string<))) - (when (and (natnump ,count) (> ,count 0)) - (setq result (tramp-compat-ntake ,count result))) - result)))) - - ;; Error handling. - (if (not (file-exists-p ,directory)) - (tramp-error - (tramp-dissect-file-name ,directory) 'file-missing ,directory) - nil))) + `(with-parsed-tramp-file-name (expand-file-name ,directory) nil + (tramp-barf-if-file-missing v ,directory + (if (not (file-directory-p ,directory)) + ;; Trigger the `file-missing' error. + (signal 'error nil) + (setf ,directory + (file-name-as-directory (expand-file-name ,directory))) + (let ((temp + (with-tramp-file-property v localname "directory-files" ,@body)) + result item) + (while temp + (setq item (directory-file-name (pop temp))) + (when (or (null ,match) (string-match-p ,match item)) + (push (if ,full (concat ,directory item) item) + result))) + (unless ,nosort + (setq result (sort result #'string<))) + (when (and (natnump ,count) (> ,count 0)) + (setq result (tramp-compat-ntake ,count result))) + result))))) (defmacro tramp-skeleton-directory-files-and-attributes (directory &optional full match nosort id-format count &rest body) "Skeleton for `tramp-*-handle-directory-files-and-attributes'. BODY is the backend specific code." (declare (indent 6) (debug t)) - `(or - (with-parsed-tramp-file-name (expand-file-name ,directory) nil - (tramp-barf-if-file-missing v ,directory - (when (file-directory-p ,directory) - (let ((temp - (copy-tree - (mapcar - (lambda (x) - (cons - (car x) - (tramp-convert-file-attributes - v (expand-file-name (car x) localname) - ,id-format (cdr x)))) - (with-tramp-file-property - v localname "directory-files-and-attributes" - ,@body)))) - result item) - - (while temp - (setq item (pop temp)) - (when (or (null ,match) (string-match-p ,match (car item))) - (when ,full - (setcar item (expand-file-name (car item) ,directory))) - (push item result))) - - (unless ,nosort - (setq result - (sort result (lambda (x y) (string< (car x) (car y)))))) - - (when (and (natnump ,count) (> ,count 0)) - (setq result (tramp-compat-ntake ,count result))) - - (or result - ;; The scripts could fail, for example with huge file size. - (tramp-handle-directory-files-and-attributes - ,directory ,full ,match ,nosort ,id-format ,count)))))) - - ;; Error handling. - (if (not (file-exists-p ,directory)) - (tramp-error - (tramp-dissect-file-name ,directory) 'file-missing ,directory) - nil))) + `(with-parsed-tramp-file-name (expand-file-name ,directory) nil + (tramp-barf-if-file-missing v ,directory + (if (not (file-directory-p ,directory)) + ;; Trigger the `file-missing' error. + (signal 'error nil) + (let ((temp + (copy-tree + (mapcar + (lambda (x) + (cons + (car x) + (tramp-convert-file-attributes + v (expand-file-name (car x) localname) + ,id-format (cdr x)))) + (with-tramp-file-property + v localname "directory-files-and-attributes" + ,@body)))) + result item) + + (while temp + (setq item (pop temp)) + (when (or (null ,match) (string-match-p ,match (car item))) + (when ,full + (setcar item (expand-file-name (car item) ,directory))) + (push item result))) + + (unless ,nosort + (setq result + (sort result (lambda (x y) (string< (car x) (car y)))))) + + (when (and (natnump ,count) (> ,count 0)) + (setq result (tramp-compat-ntake ,count result))) + + (or result + ;; The scripts could fail, for example with huge file size. + (tramp-handle-directory-files-and-attributes + ,directory ,full ,match ,nosort ,id-format ,count))))))) (defcustom tramp-use-file-attributes t "Whether to use \"file-attributes\" connection property for check. @@ -3845,20 +3843,23 @@ BODY is the backend specific code." BODY is the backend specific code." (declare (indent 1) (debug t)) `(with-parsed-tramp-file-name (expand-file-name ,filename) nil - (when (not (file-exists-p ,filename)) - (tramp-error v 'file-missing ,filename)) - (with-tramp-saved-file-properties - v localname - ;; We cannot add "file-attributes", "file-executable-p", - ;; "file-ownership-preserved-p", "file-readable-p", - ;; "file-writable-p". - '("file-directory-p" "file-exists-p" "file-symlink-p" "file-truename") - (tramp-flush-file-properties v localname)) - (condition-case err - (progn ,@body) - (error (if tramp-inhibit-errors-if-setting-file-attributes-fail - (display-warning 'tramp (error-message-string err)) - (signal (car err) (cdr err))))))) + (tramp-barf-if-file-missing v ,filename + (if (not (file-exists-p ,filename)) + ;; Trigger the `file-missing' error. + (signal 'error nil) + (with-tramp-saved-file-properties + v localname + ;; We cannot add "file-attributes", "file-executable-p", + ;; "file-ownership-preserved-p", "file-readable-p", + ;; "file-writable-p". + '("file-directory-p" "file-exists-p" + "file-symlink-p" "file-truename") + (tramp-flush-file-properties v localname)) + (condition-case err + (progn ,@body) + (error (if tramp-inhibit-errors-if-setting-file-attributes-fail + (display-warning 'tramp (error-message-string err)) + (signal (car err) (cdr err))))))))) (defmacro tramp-skeleton-write-region (start end filename append visit lockname mustbenew &rest body) @@ -3933,7 +3934,7 @@ BODY is the backend specific code." (let (last-coding-system-used (need-chown t)) ;; Set file modification time. (when (or (eq ,visit t) (stringp ,visit)) - (when-let ((file-attr (file-attributes filename 'integer))) + (when-let* ((file-attr (file-attributes filename 'integer))) (set-visited-file-modtime ;; We must pass modtime explicitly, because FILENAME ;; can be different from (buffer-file-name), f.e. if @@ -4046,9 +4047,7 @@ Let-bind it when necessary.") (tramp-dont-suspend-timers t)) (with-tramp-timeout (timeout - (unless (when-let ((p (tramp-get-connection-process v))) - (and (process-live-p p) - (tramp-get-connection-property p "connected"))) + (unless (and (not non-essential) (tramp-connectable-p v)) (tramp-cleanup-connection v 'keep-debug 'keep-password)) (tramp-error v 'file-error @@ -4227,8 +4226,8 @@ Let-bind it when necessary.") (defun tramp-handle-file-modes (filename &optional flag) "Like `file-modes' for Tramp files." - (when-let ((attrs (file-attributes filename)) - (mode-string (file-attribute-modes attrs))) + (when-let* ((attrs (file-attributes filename)) + (mode-string (file-attribute-modes attrs))) (if (and (not (eq flag 'nofollow)) (eq ?l (aref mode-string 0))) (file-modes (file-truename filename)) (tramp-mode-string-to-int mode-string)))) @@ -4379,7 +4378,7 @@ Let-bind it when necessary.") ;; because `file-truename' could raise an error for cyclic ;; symlinks. (ignore-errors - (when-let ((attr (file-attributes filename))) + (when-let* ((attr (file-attributes filename))) (cond ((eq ?- (aref (file-attribute-modes attr) 0))) ((eq ?l (aref (file-attribute-modes attr) 0)) @@ -4826,7 +4825,7 @@ It is not guaranteed, that all process attributes as described in (defun tramp-get-lock-file (file) "Read lockfile info of FILE. Return nil when there is no lockfile." - (when-let ((lockname (make-lock-file-name file))) + (when-let* ((lockname (make-lock-file-name file))) (or (file-symlink-p lockname) (and (file-readable-p lockname) (with-temp-buffer @@ -4857,8 +4856,8 @@ Do not set it manually, it is used buffer-local in `tramp-get-lock-pid'.") (defun tramp-handle-file-locked-p (file) "Like `file-locked-p' for Tramp files." - (when-let ((info (tramp-get-lock-file file)) - (match (string-match tramp-lock-file-info-regexp info))) + (when-let* ((info (tramp-get-lock-file file)) + (match (string-match tramp-lock-file-info-regexp info))) (or ; Locked by me. (and (string-equal (match-string 1 info) (user-login-name)) (string-equal (match-string 2 info) tramp-system-name) @@ -4880,20 +4879,20 @@ Do not set it manually, it is used buffer-local in `tramp-get-lock-pid'.") ;; for remote files. (ask-user-about-supersession-threat file)) - (when-let ((info (tramp-get-lock-file file)) - (match (string-match tramp-lock-file-info-regexp info))) + (when-let* ((info (tramp-get-lock-file file)) + (match (string-match tramp-lock-file-info-regexp info))) (unless (ask-user-about-lock file (format "%s@%s (pid %s)" (match-string 1 info) (match-string 2 info) (match-string 3 info))) (throw 'dont-lock nil))) - (when-let ((lockname (make-lock-file-name file)) - ;; USER@HOST.PID[:BOOT_TIME] - (info - (format - "%s@%s.%s" (user-login-name) tramp-system-name - (tramp-get-lock-pid file)))) + (when-let* ((lockname (make-lock-file-name file)) + ;; USER@HOST.PID[:BOOT_TIME] + (info + (format + "%s@%s.%s" (user-login-name) tramp-system-name + (tramp-get-lock-pid file)))) ;; Protect against security hole. (with-parsed-tramp-file-name file nil @@ -4933,9 +4932,10 @@ Do not set it manually, it is used buffer-local in `tramp-get-lock-pid'.") ;; When there is no connection, we don't do it. Otherwise, ;; functions like `kill-buffer' would try to reestablish the ;; connection. See Bug#61663. - (if-let ((v (tramp-dissect-file-name file)) - ((process-live-p (tramp-get-process v))) - (lockname (make-lock-file-name file))) + (if-let* ((v (tramp-dissect-file-name file)) + ((tramp-connectable-p v)) + ((process-live-p (tramp-get-process v))) + (lockname (make-lock-file-name file))) (delete-file lockname) ;; Trigger the unlock error. Be quiet if user isn't ;; interested in lock files. See Bug#70900. @@ -5209,13 +5209,13 @@ should be set connection-local.") elt (default-toplevel-value 'process-environment)))) (setq env (cons elt env))))) ;; Add remote path if exists. - (env (if-let ((sh-file-name-handler-p) - (remote-path - (string-join (tramp-get-remote-path v) ":"))) + (env (if-let* ((sh-file-name-handler-p) + (remote-path + (string-join (tramp-get-remote-path v) ":"))) (setenv-internal env "PATH" remote-path 'keep) env)) ;; Add HISTFILE if indicated. - (env (if-let ((sh-file-name-handler-p)) + (env (if sh-file-name-handler-p (cond ((stringp tramp-histfile-override) (setenv-internal @@ -6026,7 +6026,8 @@ performed successfully. Any other value means an error." (save-restriction (with-tramp-progress-reporter proc 3 "Waiting for prompts from remote shell" - (let (exit) + (let ((enable-recursive-minibuffers t) + exit) (with-tramp-timeout (timeout (setq exit 'timeout)) (while (not exit) (setq exit (catch 'tramp-action @@ -6101,8 +6102,8 @@ If the user quits via `C-g', it is propagated up to `tramp-file-name-handler'." ;; communication. This could block the output for the current ;; process. Read such output first. (Bug#61350) ;; The process property isn't set anymore due to Bug#62194. - (when-let (((process-get proc 'tramp-shared-socket)) - (v (process-get proc 'tramp-vector))) + (when-let* (((process-get proc 'tramp-shared-socket)) + (v (process-get proc 'tramp-vector))) (dolist (p (delq proc (process-list))) (when (tramp-file-name-equal-p v (process-get p 'tramp-vector)) (with-tramp-suspended-timers @@ -6421,10 +6422,10 @@ depending whether FILENAME is remote or local. Both parameters must be non-negative integers. The setgid bit of the upper directory is respected. If FILENAME is remote, a file name handler is called." - (let* ((dir (file-name-directory filename)) - (modes (file-modes dir))) - (when (and modes (not (zerop (logand modes #o2000)))) - (setq gid (file-attribute-group-id (file-attributes dir))))) + (when-let* ((dir (file-name-directory filename)) + (modes (file-modes dir)) + ((not (zerop (logand modes #o2000))))) + (setq gid (file-attribute-group-id (file-attributes dir)))) (if (tramp-tramp-file-p filename) (funcall (if (tramp-crypt-file-name-p filename) @@ -6948,13 +6949,15 @@ verbosity of 6." (catch 'result (let ((default-directory temporary-file-directory)) (dolist (pid (list-system-processes)) - (when-let ((attributes (process-attributes pid)) - (comm (cdr (assoc 'comm attributes)))) - (and (string-equal (cdr (assoc 'user attributes)) (user-login-name)) - ;; The returned command name could be truncated to 15 - ;; characters. Therefore, we cannot check for `string-equal'. - (string-prefix-p comm process-name) - (throw 'result t)))))))) + (and-let* ((attributes (process-attributes pid)) + (comm (cdr (assoc 'comm attributes))) + ((string-equal + (cdr (assoc 'user attributes)) (user-login-name))) + ;; The returned command name could be truncated + ;; to 15 characters. Therefore, we cannot check + ;; for `string-equal'. + ((string-prefix-p comm process-name)) + ((throw 'result t))))))))) ;; When calling "emacs -Q", `auth-source-search' won't be called. If ;; you want to debug exactly this case, call "emacs -Q --eval '(setq diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index 9a7213a8a92..4dccc970aa7 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -67,6 +67,7 @@ (require 'vc-git) (require 'vc-hg) +(declare-function project-mode-line-format "project") (declare-function tramp-check-remote-uname "tramp-sh") (declare-function tramp-find-executable "tramp-sh") (declare-function tramp-get-remote-chmod-h "tramp-sh") @@ -89,6 +90,7 @@ (defvar tramp-use-connection-share) ;; Declared in Emacs 30.1. +(defvar project-mode-line) (defvar remote-file-name-access-timeout) (defvar remote-file-name-inhibit-delete-by-moving-to-trash) @@ -8015,8 +8017,52 @@ process sentinels. They shall not disturb each other." ;; Cleanup. (tramp-cleanup-connection tramp-test-vec 'keep-debug)) +;; This test is inspired by Bug#78572. +(ert-deftest tramp-test48-session-timeout () + "Check that Tramp handles a session timeout properly." + (skip-unless (tramp--test-enabled)) + (skip-unless + (tramp-get-method-parameter tramp-test-vec 'tramp-session-timeout)) + + ;; We want to see the timeout message. + (tramp--test-instrument-test-case 3 + (let ((remote-file-name-inhibit-cache t) + (tmp-name (tramp--test-make-temp-name))) + (unwind-protect + (progn + (should-not (file-exists-p tmp-name)) + (write-region "foo" nil tmp-name) + (should (file-exists-p tmp-name)) + + (tramp-timeout-session tramp-test-vec) + (should (file-exists-p tmp-name)) + (should (directory-files (file-name-directory tmp-name))) + + ;; `project-mode-line' was introduced in Emacs 30.1. + (when (boundp 'project-mode-line) + (require 'project) + (ert-with-message-capture captured-messages + (let ((project-mode-line t)) + (with-temp-buffer + (set-visited-file-name tmp-name) + (insert "foo") + (should (buffer-modified-p)) + (tramp-timeout-session tramp-test-vec) + ;; This calls `file-directory-p' and + ;; `directory-files'. Shouldn't raise an error when + ;; not connected. + (project-mode-line-format) + ;; Steal the file lock. + (cl-letf (((symbol-function #'ask-user-about-lock) #'always)) + (save-buffer))) + (should-not + (string-match-p "File is missing:" captured-messages)))))) + + ;; Cleanup. + (ignore-errors (delete-file tmp-name)))))) + ;; This test is inspired by Bug#29163. -(ert-deftest tramp-test48-auto-load () +(ert-deftest tramp-test49-auto-load () "Check that Tramp autoloads properly." ;; If we use another syntax but `default', Tramp is already loaded ;; due to the `tramp-change-syntax' call. @@ -8041,7 +8087,7 @@ process sentinels. They shall not disturb each other." (mapconcat #'shell-quote-argument load-path " -L ") (shell-quote-argument code))))))) -(ert-deftest tramp-test48-delay-load () +(ert-deftest tramp-test49-delay-load () "Check that Tramp is loaded lazily, only when needed." ;; Tramp is neither loaded at Emacs startup, nor when completing a ;; non-Tramp file name like "/foo". Completing a Tramp-alike file @@ -8071,7 +8117,7 @@ process sentinels. They shall not disturb each other." (mapconcat #'shell-quote-argument load-path " -L ") (shell-quote-argument (format code tm))))))))) -(ert-deftest tramp-test48-recursive-load () +(ert-deftest tramp-test49-recursive-load () "Check that Tramp does not fail due to recursive load." (skip-unless (tramp--test-enabled)) @@ -8095,7 +8141,7 @@ process sentinels. They shall not disturb each other." (mapconcat #'shell-quote-argument load-path " -L ") (shell-quote-argument code)))))))) -(ert-deftest tramp-test48-remote-load-path () +(ert-deftest tramp-test49-remote-load-path () "Check that Tramp autoloads its packages with remote `load-path'." ;; `tramp-cleanup-all-connections' is autoloaded from tramp-cmds.el. ;; It shall still work, when a remote file name is in the @@ -8120,7 +8166,7 @@ process sentinels. They shall not disturb each other." (mapconcat #'shell-quote-argument load-path " -L ") (shell-quote-argument code))))))) -(ert-deftest tramp-test49-without-remote-files () +(ert-deftest tramp-test50-without-remote-files () "Check that Tramp can be suppressed." (skip-unless (tramp--test-enabled)) @@ -8135,7 +8181,7 @@ process sentinels. They shall not disturb each other." (setq tramp-mode t) (should (file-remote-p ert-remote-temporary-file-directory))) -(ert-deftest tramp-test50-unload () +(ert-deftest tramp-test51-unload () "Check that Tramp and its subpackages unload completely. Since it unloads Tramp, it shall be the last test to run." :tags '(:expensive-test) -- 2.39.5