1;;; tramp-tests.el --- Tests of remote file access  -*- lexical-binding:t -*-
2
3;; Copyright (C) 2013-2021 Free Software Foundation, Inc.
4
5;; Author: Michael Albinus <michael.albinus@gmx.de>
6
7;; This file is part of GNU Emacs.
8;;
9;; GNU Emacs is free software: you can redistribute it and/or
10;; modify it under the terms of the GNU General Public License as
11;; published by the Free Software Foundation, either version 3 of the
12;; License, or (at your option) any later version.
13;;
14;; GNU Emacs is distributed in the hope that it will be useful, but
15;; WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17;; General Public License for more details.
18;;
19;; You should have received a copy of the GNU General Public License
20;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
21
22;;; Commentary:
23
24;; Some of the tests require access to a remote host files.  Since
25;; this could be problematic, a mock-up connection method "mock" is
26;; used.  Emulating a remote connection, it simply calls "sh -i".
27;; Tramp's file name handlers still run, so this test is sufficient
28;; except for connection establishing.
29
30;; If you want to test a real Tramp connection, set
31;; $REMOTE_TEMPORARY_FILE_DIRECTORY to a suitable value in order to
32;; overwrite the default value.  If you want to skip tests accessing a
33;; remote host, set this environment variable to "/dev/null" or
34;; whatever is appropriate on your system.
35
36;; For slow remote connections, `tramp-test44-asynchronous-requests'
37;; might be too heavy.  Setting $REMOTE_PARALLEL_PROCESSES to a proper
38;; value less than 10 could help.
39
40;; A whole test run can be performed calling the command `tramp-test-all'.
41
42;;; Code:
43
44(require 'cl-lib)
45(require 'dired)
46(require 'dired-aux)
47(require 'ert)
48(require 'ert-x)
49(require 'seq) ; For `seq-random-elt', autoloaded since Emacs 28.1
50(require 'trace)
51(require 'tramp)
52(require 'vc)
53(require 'vc-bzr)
54(require 'vc-git)
55(require 'vc-hg)
56
57(declare-function tramp-check-remote-uname "tramp-sh")
58(declare-function tramp-find-executable "tramp-sh")
59(declare-function tramp-get-remote-chmod-h "tramp-sh")
60(declare-function tramp-get-remote-path "tramp-sh")
61(declare-function tramp-get-remote-perl "tramp-sh")
62(declare-function tramp-get-remote-stat "tramp-sh")
63(declare-function tramp-list-tramp-buffers "tramp-cmds")
64(declare-function tramp-method-out-of-band-p "tramp-sh")
65(declare-function tramp-smb-get-localname "tramp-smb")
66(defvar ange-ftp-make-backup-files)
67(defvar auto-save-file-name-transforms)
68(defvar lock-file-name-transforms)
69(defvar remote-file-name-inhibit-locks)
70(defvar tramp-connection-properties)
71(defvar tramp-copy-size-limit)
72(defvar tramp-display-escape-sequence-regexp)
73(defvar tramp-fuse-unmount-on-cleanup)
74(defvar tramp-inline-compress-start-size)
75(defvar tramp-persistency-file-name)
76(defvar tramp-remote-path)
77(defvar tramp-remote-process-environment)
78
79;; Needed for Emacs 27.
80(defvar process-file-return-signal-string)
81(defvar shell-command-dont-erase-buffer)
82;; Needed for Emacs 28.
83(defvar dired-copy-dereference)
84
85;; Beautify batch mode.
86(when noninteractive
87  ;; Suppress nasty messages.
88  (fset #'shell-command-sentinel #'ignore)
89  ;; We do not want to be interrupted.
90  (eval-after-load 'tramp-gvfs
91    '(fset 'tramp-gvfs-handler-askquestion
92	   (lambda (_message _choices) '(t nil 0)))))
93
94;; There is no default value on w32 systems, which could work out of the box.
95(defconst tramp-test-temporary-file-directory
96  (cond
97   ((getenv "REMOTE_TEMPORARY_FILE_DIRECTORY"))
98   ((eq system-type 'windows-nt) null-device)
99   (t (add-to-list
100       'tramp-methods
101       '("mock"
102	 (tramp-login-program        "sh")
103	 (tramp-login-args           (("-i")))
104	 (tramp-remote-shell         "/bin/sh")
105	 (tramp-remote-shell-args    ("-c"))
106	 (tramp-connection-timeout   10)))
107      (add-to-list
108       'tramp-default-host-alist
109       `("\\`mock\\'" nil ,(system-name)))
110      ;; Emacs's Makefile sets $HOME to a nonexistent value.  Needed
111      ;; in batch mode only, therefore.
112      (unless (and (null noninteractive) (file-directory-p "~/"))
113        (setenv "HOME" temporary-file-directory))
114      (format "/mock::%s" temporary-file-directory)))
115  "Temporary directory for Tramp tests.")
116
117(defconst tramp-test-vec
118  (and (file-remote-p tramp-test-temporary-file-directory)
119       (tramp-dissect-file-name tramp-test-temporary-file-directory))
120  "The used `tramp-file-name' structure.")
121
122(setq auth-source-save-behavior nil
123      password-cache-expiry nil
124      remote-file-name-inhibit-cache nil
125      tramp-allow-unsafe-temporary-files t
126      tramp-cache-read-persistent-data t ;; For auth-sources.
127      tramp-copy-size-limit nil
128      tramp-persistency-file-name nil
129      tramp-verbose 0)
130
131;; This should happen on hydra only.
132(when (getenv "EMACS_HYDRA_CI")
133  (add-to-list 'tramp-remote-path 'tramp-own-remote-path))
134
135(defvar tramp--test-enabled-checked nil
136  "Cached result of `tramp--test-enabled'.
137If the function did run, the value is a cons cell, the `cdr'
138being the result.")
139
140(defun tramp--test-enabled ()
141  "Whether remote file access is enabled."
142  (unless (consp tramp--test-enabled-checked)
143    (setq
144     tramp--test-enabled-checked
145     (cons
146      t (ignore-errors
147	  (and
148	   (file-remote-p tramp-test-temporary-file-directory)
149	   (file-directory-p tramp-test-temporary-file-directory)
150	   (file-writable-p tramp-test-temporary-file-directory))))))
151
152  (when (cdr tramp--test-enabled-checked)
153    ;; Cleanup connection.
154    (ignore-errors
155      (tramp-cleanup-connection tramp-test-vec nil 'keep-password)))
156
157  ;; Return result.
158  (cdr tramp--test-enabled-checked))
159
160(defun tramp--test-make-temp-name (&optional local quoted)
161  "Return a temporary file name for test.
162If LOCAL is non-nil, a local file name is returned.
163If QUOTED is non-nil, the local part of the file name is quoted.
164The temporary file is not created."
165  (funcall
166   (if quoted #'tramp-compat-file-name-quote #'identity)
167   (expand-file-name
168    (make-temp-name "tramp-test")
169    (if local temporary-file-directory tramp-test-temporary-file-directory))))
170
171;; Method "smb" supports `make-symbolic-link' only if the remote host
172;; has CIFS capabilities.  tramp-adb.el, tramp-gvfs.el, tramp-rclone.el
173;; and tramp-sshfs.el do not support symbolic links at all.
174(defmacro tramp--test-ignore-make-symbolic-link-error (&rest body)
175  "Run BODY, ignoring \"make-symbolic-link not supported\" file error."
176  (declare (indent defun) (debug (body)))
177  `(condition-case err
178       (progn ,@body)
179     (file-error
180      (unless (string-equal (error-message-string err)
181			    "make-symbolic-link not supported")
182	(signal (car err) (cdr err))))))
183
184;; Don't print messages in nested `tramp--test-instrument-test-case' calls.
185(defvar tramp--test-instrument-test-case-p nil
186  "Whether `tramp--test-instrument-test-case' run.
187This shall used dynamically bound only.")
188
189;; When `tramp-verbose' is greater than 10, and you want to trace
190;; other functions as well, do something like
191;; (let ((tramp-trace-functions '(file-name-non-special)))
192;;   (tramp--test-instrument-test-case 11
193;;     ...))
194(defmacro tramp--test-instrument-test-case (verbose &rest body)
195  "Run BODY with `tramp-verbose' equal VERBOSE.
196Print the content of the Tramp connection and debug buffers, if
197`tramp-verbose' is greater than 3.  Print traces if `tramp-verbose'
198is greater than 10.
199`should-error' is not handled properly.  BODY shall not contain a timeout."
200  (declare (indent 1) (debug (natnump body)))
201  `(let* ((tramp-verbose (max (or ,verbose 0) (or tramp-verbose 0)))
202	  (trace-buffer (tramp-trace-buffer-name tramp-test-vec))
203	  (debug-ignored-errors
204	   (append
205	    '("^make-symbolic-link not supported$"
206	      "^error with add-name-to-file")
207	    debug-ignored-errors))
208	  inhibit-message)
209     (unwind-protect
210	 (let ((tramp--test-instrument-test-case-p t)) ,@body)
211       ;; Unwind forms.
212       (when (and (null tramp--test-instrument-test-case-p) (> tramp-verbose 3))
213	 (untrace-all)
214	 (dolist (buf (tramp-list-tramp-buffers))
215	   (message ";; %s\n%s" buf (tramp-get-buffer-string buf))
216	   (kill-buffer buf))))))
217
218(defsubst tramp--test-message (fmt-string &rest arguments)
219  "Emit a message into ERT *Messages*."
220  (tramp--test-instrument-test-case 0
221    (apply #'tramp-message tramp-test-vec 0 fmt-string arguments)))
222
223(defsubst tramp--test-backtrace ()
224  "Dump a backtrace into ERT *Messages*."
225  (tramp--test-instrument-test-case 10
226    (tramp-backtrace tramp-test-vec)))
227
228(defmacro tramp--test-print-duration (message &rest body)
229  "Run BODY and print a message with duration, prompted by MESSAGE."
230  (declare (indent 1) (debug (stringp body)))
231  `(let ((start (current-time)))
232     (unwind-protect
233	 (progn ,@body)
234       (tramp--test-message
235	"%s %f sec" ,message (float-time (time-subtract nil start))))))
236
237;; `always' is introduced with Emacs 28.1.
238(defalias 'tramp--test-always
239  (if (fboundp 'always)
240      #'always
241    (lambda (&rest _arguments)
242      "Do nothing and return t.
243This function accepts any number of ARGUMENTS, but ignores them.
244Also see `ignore'."
245      t)))
246
247(ert-deftest tramp-test00-availability ()
248  "Test availability of Tramp functions."
249  :expected-result (if (tramp--test-enabled) :passed :failed)
250  (tramp--test-message
251   "Remote directory: `%s'" tramp-test-temporary-file-directory)
252  (should (ignore-errors
253	    (and
254	     (file-remote-p tramp-test-temporary-file-directory)
255	     (file-directory-p tramp-test-temporary-file-directory)
256	     (file-writable-p tramp-test-temporary-file-directory)))))
257
258(ert-deftest tramp-test01-file-name-syntax ()
259  "Check remote file name syntax."
260  (let ((syntax tramp-syntax))
261    (unwind-protect
262	(progn
263	  (tramp-change-syntax 'default)
264	  ;; Simple cases.
265	  (should (tramp-tramp-file-p "/method::"))
266	  (should (tramp-tramp-file-p "/method:host:"))
267	  (should (tramp-tramp-file-p "/method:user@:"))
268	  (should (tramp-tramp-file-p "/method:user@host:"))
269	  (should (tramp-tramp-file-p "/method:user@email@host:"))
270
271	  ;; Using a port.
272	  (should (tramp-tramp-file-p "/method:host#1234:"))
273	  (should (tramp-tramp-file-p "/method:user@host#1234:"))
274
275	  ;; Using an IPv4 address.
276	  (should (tramp-tramp-file-p "/method:1.2.3.4:"))
277	  (should (tramp-tramp-file-p "/method:user@1.2.3.4:"))
278
279	  ;; Using an IPv6 address.
280	  (should (tramp-tramp-file-p "/method:[::1]:"))
281	  (should (tramp-tramp-file-p "/method:user@[::1]:"))
282
283	  ;; Using an IPv4 mapped IPv6 address.
284	  (should (tramp-tramp-file-p "/method:[::ffff:1.2.3.4]:"))
285	  (should (tramp-tramp-file-p "/method:user@[::ffff:1.2.3.4]:"))
286
287	  ;; Local file name part.
288	  (should (tramp-tramp-file-p "/method:::"))
289	  (should (tramp-tramp-file-p "/method::/:"))
290	  (should (tramp-tramp-file-p "/method::/path/to/file"))
291	  (should (tramp-tramp-file-p "/method::/:/path/to/file"))
292	  (should (tramp-tramp-file-p "/method::file"))
293	  (should (tramp-tramp-file-p "/method::/:file"))
294
295	  ;; Multihop.
296	  (should (tramp-tramp-file-p "/method1:|method2::"))
297	  (should
298	   (tramp-tramp-file-p "/method1:host1|method2:host2:"))
299	  (should
300	   (tramp-tramp-file-p "/method1:user1@host1|method2:user2@host2:"))
301	  (should
302	   (tramp-tramp-file-p
303	    "/method1:user1@host1|method2:user2@host2|method3:user3@host3:"))
304
305	  ;; No strings.
306	  (should-not (tramp-tramp-file-p nil))
307	  (should-not (tramp-tramp-file-p 'symbol))
308	  ;; No newline or linefeed.
309	  (should-not (tramp-tramp-file-p "/method::file\nname"))
310	  (should-not (tramp-tramp-file-p "/method::file\rname"))
311	  ;; Ange-FTP syntax.
312	  (should-not (tramp-tramp-file-p "/host:"))
313	  (should-not (tramp-tramp-file-p "/user@host:"))
314	  (should-not (tramp-tramp-file-p "/1.2.3.4:"))
315	  (should-not (tramp-tramp-file-p "/[]:"))
316	  (should-not (tramp-tramp-file-p "/[::1]:"))
317	  (should-not (tramp-tramp-file-p "/[::ffff:1.2.3.4]:"))
318	  (should-not (tramp-tramp-file-p "/host:/:"))
319	  (should-not (tramp-tramp-file-p "/host1|host2:"))
320	  (should-not (tramp-tramp-file-p "/user1@host1|user2@host2:"))
321	  ;; Quote with "/:" suppresses file name handlers.
322	  (should-not (tramp-tramp-file-p "/::"))
323	  (should-not (tramp-tramp-file-p "/:@:"))
324	  (should-not (tramp-tramp-file-p "/:[]:"))
325	  ;; When `tramp-mode' is nil, Tramp is not activated.
326	  (let (tramp-mode)
327	    (should-not (tramp-tramp-file-p "/method:user@host:")))
328	  ;; `tramp-ignored-file-name-regexp' suppresses Tramp.
329	  (let ((tramp-ignored-file-name-regexp "^/method:user@host:"))
330	    (should-not (tramp-tramp-file-p "/method:user@host:")))
331	  ;; Methods shall be at least two characters on MS Windows,
332	  ;; except the default method.
333	  (let ((system-type 'windows-nt))
334	    (should-not (tramp-tramp-file-p "/c:/path/to/file"))
335	    (should-not (tramp-tramp-file-p "/c::/path/to/file"))
336	    (should (tramp-tramp-file-p "/-::/path/to/file")))
337	  (let ((system-type 'gnu/linux))
338	    (should (tramp-tramp-file-p "/-:h:/path/to/file"))
339	    (should (tramp-tramp-file-p "/m::/path/to/file"))))
340
341      ;; Exit.
342      (tramp-change-syntax syntax))))
343
344(ert-deftest tramp-test01-file-name-syntax-simplified ()
345  "Check simplified file name syntax."
346  :tags '(:expensive-test)
347  (let ((syntax tramp-syntax))
348    (unwind-protect
349	(progn
350	  (tramp-change-syntax 'simplified)
351	  ;; Simple cases.
352	  (should (tramp-tramp-file-p "/host:"))
353	  (should (tramp-tramp-file-p "/user@:"))
354	  (should (tramp-tramp-file-p "/user@host:"))
355	  (should (tramp-tramp-file-p "/user@email@host:"))
356
357	  ;; Using a port.
358	  (should (tramp-tramp-file-p "/host#1234:"))
359	  (should (tramp-tramp-file-p "/user@host#1234:"))
360
361	  ;; Using an IPv4 address.
362	  (should (tramp-tramp-file-p "/1.2.3.4:"))
363	  (should (tramp-tramp-file-p "/user@1.2.3.4:"))
364
365	  ;; Using an IPv6 address.
366	  (should (tramp-tramp-file-p "/[::1]:"))
367	  (should (tramp-tramp-file-p "/user@[::1]:"))
368
369	  ;; Using an IPv4 mapped IPv6 address.
370	  (should (tramp-tramp-file-p "/[::ffff:1.2.3.4]:"))
371	  (should (tramp-tramp-file-p "/user@[::ffff:1.2.3.4]:"))
372
373	  ;; Local file name part.
374	  (should (tramp-tramp-file-p "/host::"))
375	  (should (tramp-tramp-file-p "/host:/:"))
376	  (should (tramp-tramp-file-p "/host:/path/to/file"))
377	  (should (tramp-tramp-file-p "/host:/:/path/to/file"))
378	  (should (tramp-tramp-file-p "/host:file"))
379	  (should (tramp-tramp-file-p "/host:/:file"))
380
381	  ;; Multihop.
382	  (should (tramp-tramp-file-p "/host1|host2:"))
383	  (should (tramp-tramp-file-p "/user1@host1|user2@host2:"))
384	  (should (tramp-tramp-file-p "/user1@host1|user2@host2|user3@host3:"))
385
386	  ;; No strings.
387	  (should-not (tramp-tramp-file-p nil))
388	  (should-not (tramp-tramp-file-p 'symbol))
389	  ;; Quote with "/:" suppresses file name handlers.
390	  (should-not (tramp-tramp-file-p "/::"))
391	  (should-not (tramp-tramp-file-p "/:@:"))
392	  (should-not (tramp-tramp-file-p "/:[]:")))
393
394      ;; Exit.
395      (tramp-change-syntax syntax))))
396
397(ert-deftest tramp-test01-file-name-syntax-separate ()
398  "Check separate file name syntax."
399  :tags '(:expensive-test)
400  (let ((syntax tramp-syntax))
401    (unwind-protect
402	(progn
403	  (tramp-change-syntax 'separate)
404	  ;; Simple cases.
405	  (should (tramp-tramp-file-p "/[method/]"))
406	  (should (tramp-tramp-file-p "/[method/host]"))
407	  (should (tramp-tramp-file-p "/[method/user@]"))
408	  (should (tramp-tramp-file-p "/[method/user@host]"))
409	  (should (tramp-tramp-file-p "/[method/user@email@host]"))
410
411	  ;; Using a port.
412	  (should (tramp-tramp-file-p "/[method/host#1234]"))
413	  (should (tramp-tramp-file-p "/[method/user@host#1234]"))
414
415	  ;; Using an IPv4 address.
416	  (should (tramp-tramp-file-p "/[method/1.2.3.4]"))
417	  (should (tramp-tramp-file-p "/[method/user@1.2.3.4]"))
418
419	  ;; Using an IPv6 address.
420	  (should (tramp-tramp-file-p "/[method/::1]"))
421	  (should (tramp-tramp-file-p "/[method/user@::1]"))
422
423	  ;; Using an IPv4 mapped IPv6 address.
424	  (should (tramp-tramp-file-p "/[method/::ffff:1.2.3.4]"))
425	  (should (tramp-tramp-file-p "/[method/user@::ffff:1.2.3.4]"))
426
427	  ;; Local file name part.
428	  (should (tramp-tramp-file-p "/[method/]"))
429	  (should (tramp-tramp-file-p "/[method/]/:"))
430	  (should (tramp-tramp-file-p "/[method/]/path/to/file"))
431	  (should (tramp-tramp-file-p "/[method/]/:/path/to/file"))
432	  (should (tramp-tramp-file-p "/[method/]file"))
433	  (should (tramp-tramp-file-p "/[method/]/:file"))
434
435	  ;; Multihop.
436	  (should (tramp-tramp-file-p "/[method1/|method2/]"))
437	  (should (tramp-tramp-file-p "/[method1/host1|method2/host2]"))
438	  (should
439	   (tramp-tramp-file-p
440	    "/[method1/user1@host1|method2/user2@host2]"))
441	  (should
442	   (tramp-tramp-file-p
443	    "/[method1/user1@host1|method2/user2@host2|method3/user3@host3]"))
444
445	  ;; No strings.
446	  (should-not (tramp-tramp-file-p nil))
447	  (should-not (tramp-tramp-file-p 'symbol))
448	  ;; Ange-FTP syntax.
449	  (should-not (tramp-tramp-file-p "/host:"))
450	  (should-not (tramp-tramp-file-p "/user@host:"))
451	  (should-not (tramp-tramp-file-p "/1.2.3.4:"))
452	  (should-not (tramp-tramp-file-p "/host:/:"))
453	  (should-not (tramp-tramp-file-p "/host1|host2:"))
454	  (should-not (tramp-tramp-file-p "/user1@host1|user2@host2:"))
455	  ;; Quote with "/:" suppresses file name handlers.
456	  (should-not (tramp-tramp-file-p "/:[]")))
457
458      ;; Exit.
459      (tramp-change-syntax syntax))))
460
461(ert-deftest tramp-test02-file-name-dissect ()
462  "Check remote file name components."
463  (let ((tramp-default-method "default-method")
464	(tramp-default-user "default-user")
465	(tramp-default-host "default-host")
466	tramp-default-method-alist
467	tramp-default-user-alist
468	tramp-default-host-alist
469	;; Suppress method name check.
470	(non-essential t)
471	;; Suppress check for multihops.
472	(tramp-cache-data (make-hash-table :test #'equal))
473	(tramp-connection-properties '((nil "login-program" t)))
474	(syntax tramp-syntax))
475    (unwind-protect
476	(progn
477	  (tramp-change-syntax 'default)
478	  ;; An unknown method shall raise an error.
479	  (let (non-essential)
480	    (should-error
481	     (expand-file-name "/method:user@host:")
482	     :type 'user-error))
483
484	  ;; Expand `tramp-default-user' and `tramp-default-host'.
485	  (should
486	   (string-equal
487	    (file-remote-p "/method::")
488	    (format "/%s:%s@%s:" "method" "default-user" "default-host")))
489	  (should (string-equal (file-remote-p "/method::" 'method) "method"))
490	  (should
491	   (string-equal (file-remote-p "/method::" 'user) "default-user"))
492	  (should
493	   (string-equal (file-remote-p "/method::" 'host) "default-host"))
494	  (should (string-equal (file-remote-p "/method::" 'localname) ""))
495	  (should (string-equal (file-remote-p "/method::" 'hop) nil))
496
497	  ;; Expand `tramp-default-method' and `tramp-default-user'.
498	  (should
499	   (string-equal
500	    (file-remote-p "/-:host:")
501	    (format "/%s:%s@%s:" "default-method" "default-user" "host")))
502	  (should
503	   (string-equal (file-remote-p "/-:host:" 'method) "default-method"))
504	  (should
505	   (string-equal (file-remote-p "/-:host:" 'user) "default-user"))
506	  (should (string-equal (file-remote-p "/-:host:" 'host) "host"))
507	  (should (string-equal (file-remote-p "/-:host:" 'localname) ""))
508	  (should (string-equal (file-remote-p "/-:host:" 'hop) nil))
509
510	  ;; Expand `tramp-default-method' and `tramp-default-host'.
511	  (should
512	   (string-equal
513	    (file-remote-p "/-:user@:")
514	    (format "/%s:%s@%s:" "default-method" "user" "default-host")))
515	  (should
516	   (string-equal (file-remote-p "/-:user@:" 'method) "default-method"))
517	  (should (string-equal (file-remote-p "/-:user@:" 'user) "user"))
518	  (should
519	   (string-equal (file-remote-p "/-:user@:" 'host) "default-host"))
520	  (should (string-equal (file-remote-p "/-:user@:" 'localname) ""))
521	  (should (string-equal (file-remote-p "/-:user@:" 'hop) nil))
522
523	  ;; Expand `tramp-default-method'.
524	  (should (string-equal
525		   (file-remote-p "/-:user@host:")
526		   (format "/%s:%s@%s:" "default-method" "user" "host")))
527	  (should (string-equal
528		   (file-remote-p "/-:user@host:" 'method) "default-method"))
529	  (should (string-equal (file-remote-p "/-:user@host:" 'user) "user"))
530	  (should (string-equal (file-remote-p "/-:user@host:" 'host) "host"))
531	  (should (string-equal (file-remote-p "/-:user@host:" 'localname) ""))
532	  (should (string-equal (file-remote-p "/-:user@host:" 'hop) nil))
533
534	  ;; Expand `tramp-default-user'.
535	  (should (string-equal
536		   (file-remote-p "/method:host:")
537		   (format "/%s:%s@%s:" "method" "default-user" "host")))
538	  (should
539	   (string-equal (file-remote-p "/method:host:" 'method) "method"))
540	  (should
541	   (string-equal (file-remote-p "/method:host:" 'user) "default-user"))
542	  (should (string-equal (file-remote-p "/method:host:" 'host) "host"))
543	  (should (string-equal (file-remote-p "/method:host:" 'localname) ""))
544	  (should (string-equal (file-remote-p "/method:host:" 'hop) nil))
545
546	  ;; Expand `tramp-default-host'.
547	  (should
548	   (string-equal
549	    (file-remote-p "/method:user@:")
550	    (format "/%s:%s@%s:" "method" "user" "default-host")))
551	  (should
552	   (string-equal (file-remote-p "/method:user@:" 'method) "method"))
553	  (should (string-equal (file-remote-p "/method:user@:" 'user) "user"))
554	  (should
555	   (string-equal (file-remote-p "/method:user@:" 'host) "default-host"))
556	  (should (string-equal (file-remote-p "/method:user@:" 'localname) ""))
557	  (should (string-equal (file-remote-p "/method:user@:" 'hop) nil))
558
559	  ;; No expansion.
560	  (should (string-equal
561		   (file-remote-p "/method:user@host:")
562		   (format "/%s:%s@%s:" "method" "user" "host")))
563	  (should (string-equal
564		   (file-remote-p "/method:user@host:" 'method) "method"))
565	  (should
566	   (string-equal (file-remote-p "/method:user@host:" 'user) "user"))
567	  (should
568	   (string-equal (file-remote-p "/method:user@host:" 'host) "host"))
569	  (should
570	   (string-equal (file-remote-p "/method:user@host:" 'localname) ""))
571	  (should (string-equal (file-remote-p "/method:user@host:" 'hop) nil))
572
573	  ;; No expansion.
574	  (should (string-equal
575		   (file-remote-p "/method:user@email@host:")
576		   (format "/%s:%s@%s:" "method" "user@email" "host")))
577	  (should (string-equal
578		   (file-remote-p "/method:user@email@host:" 'method) "method"))
579	  (should
580	   (string-equal
581	    (file-remote-p "/method:user@email@host:" 'user) "user@email"))
582	  (should (string-equal
583		   (file-remote-p "/method:user@email@host:" 'host) "host"))
584	  (should (string-equal
585		   (file-remote-p "/method:user@email@host:" 'localname) ""))
586	  (should (string-equal
587		   (file-remote-p "/method:user@email@host:" 'hop) nil))
588
589	  ;; Expand `tramp-default-method' and `tramp-default-user'.
590	  (should
591	   (string-equal
592	    (file-remote-p "/-:host#1234:")
593	    (format "/%s:%s@%s:" "default-method" "default-user" "host#1234")))
594	  (should (string-equal
595		   (file-remote-p "/-:host#1234:" 'method) "default-method"))
596	  (should
597	   (string-equal (file-remote-p "/-:host#1234:" 'user) "default-user"))
598	  (should
599	   (string-equal (file-remote-p "/-:host#1234:" 'host) "host#1234"))
600	  (should (string-equal (file-remote-p "/-:host#1234:" 'localname) ""))
601	  (should (string-equal (file-remote-p "/-:host#1234:" 'hop) nil))
602
603	  ;; Expand `tramp-default-method'.
604	  (should (string-equal
605		   (file-remote-p "/-:user@host#1234:")
606		   (format "/%s:%s@%s:" "default-method" "user" "host#1234")))
607	  (should
608	   (string-equal
609	    (file-remote-p "/-:user@host#1234:" 'method) "default-method"))
610	  (should
611	   (string-equal (file-remote-p "/-:user@host#1234:" 'user) "user"))
612	  (should
613	   (string-equal
614	    (file-remote-p "/-:user@host#1234:" 'host) "host#1234"))
615	  (should
616	   (string-equal (file-remote-p "/-:user@host#1234:" 'localname) ""))
617	  (should (string-equal (file-remote-p "/-:user@host#1234:" 'hop) nil))
618
619	  ;; Expand `tramp-default-user'.
620	  (should (string-equal
621		   (file-remote-p "/method:host#1234:")
622		   (format "/%s:%s@%s:" "method" "default-user" "host#1234")))
623	  (should (string-equal
624		   (file-remote-p "/method:host#1234:" 'method) "method"))
625	  (should (string-equal
626		   (file-remote-p "/method:host#1234:" 'user) "default-user"))
627	  (should (string-equal
628		   (file-remote-p "/method:host#1234:" 'host) "host#1234"))
629	  (should
630	   (string-equal (file-remote-p "/method:host#1234:" 'localname) ""))
631	  (should (string-equal (file-remote-p "/method:host#1234:" 'hop) nil))
632
633	  ;; No expansion.
634	  (should (string-equal
635		   (file-remote-p "/method:user@host#1234:")
636		   (format "/%s:%s@%s:" "method" "user" "host#1234")))
637	  (should (string-equal
638		   (file-remote-p "/method:user@host#1234:" 'method) "method"))
639	  (should (string-equal
640		   (file-remote-p "/method:user@host#1234:" 'user) "user"))
641	  (should (string-equal
642		   (file-remote-p "/method:user@host#1234:" 'host) "host#1234"))
643	  (should (string-equal
644		   (file-remote-p "/method:user@host#1234:" 'localname) ""))
645	  (should (string-equal
646		   (file-remote-p "/method:user@host#1234:" 'hop) nil))
647
648	  ;; Expand `tramp-default-method' and `tramp-default-user'.
649	  (should
650	   (string-equal
651	    (file-remote-p "/-:1.2.3.4:")
652	    (format "/%s:%s@%s:" "default-method" "default-user" "1.2.3.4")))
653	  (should
654	   (string-equal
655	    (file-remote-p "/-:1.2.3.4:" 'method) "default-method"))
656	  (should
657	   (string-equal (file-remote-p "/-:1.2.3.4:" 'user) "default-user"))
658	  (should (string-equal (file-remote-p "/-:1.2.3.4:" 'host) "1.2.3.4"))
659	  (should (string-equal (file-remote-p "/-:1.2.3.4:" 'localname) ""))
660	  (should (string-equal (file-remote-p "/-:1.2.3.4:" 'hop) nil))
661
662	  ;; Expand `tramp-default-method'.
663	  (should (string-equal
664		   (file-remote-p "/-:user@1.2.3.4:")
665		   (format "/%s:%s@%s:" "default-method" "user" "1.2.3.4")))
666	  (should (string-equal
667		   (file-remote-p "/-:user@1.2.3.4:" 'method) "default-method"))
668	  (should
669	   (string-equal (file-remote-p "/-:user@1.2.3.4:" 'user) "user"))
670	  (should
671	   (string-equal (file-remote-p "/-:user@1.2.3.4:" 'host) "1.2.3.4"))
672	  (should
673	   (string-equal (file-remote-p "/-:user@1.2.3.4:" 'localname) ""))
674	  (should
675	   (string-equal (file-remote-p "/-:user@1.2.3.4:" 'hop) nil))
676
677	  ;; Expand `tramp-default-user'.
678	  (should (string-equal
679		   (file-remote-p "/method:1.2.3.4:")
680		   (format "/%s:%s@%s:" "method" "default-user" "1.2.3.4")))
681	  (should
682	   (string-equal (file-remote-p "/method:1.2.3.4:" 'method) "method"))
683	  (should (string-equal
684		   (file-remote-p "/method:1.2.3.4:" 'user) "default-user"))
685	  (should
686	   (string-equal (file-remote-p "/method:1.2.3.4:" 'host) "1.2.3.4"))
687	  (should
688	   (string-equal (file-remote-p "/method:1.2.3.4:" 'localname) ""))
689	  (should (string-equal (file-remote-p "/method:1.2.3.4:" 'hop) nil))
690
691	  ;; No expansion.
692	  (should (string-equal
693		   (file-remote-p "/method:user@1.2.3.4:")
694		   (format "/%s:%s@%s:" "method" "user" "1.2.3.4")))
695	  (should (string-equal
696		   (file-remote-p "/method:user@1.2.3.4:" 'method) "method"))
697	  (should
698	   (string-equal (file-remote-p "/method:user@1.2.3.4:" 'user) "user"))
699	  (should (string-equal
700		   (file-remote-p "/method:user@1.2.3.4:" 'host) "1.2.3.4"))
701	  (should (string-equal
702		   (file-remote-p "/method:user@1.2.3.4:" 'localname) ""))
703	  (should (string-equal
704		   (file-remote-p "/method:user@1.2.3.4:" 'hop) nil))
705
706	  ;; Expand `tramp-default-method', `tramp-default-user' and
707	  ;; `tramp-default-host'.
708	  (should
709	   (string-equal
710	    (file-remote-p "/-:[]:")
711	    (format
712	     "/%s:%s@%s:" "default-method" "default-user" "default-host")))
713	  (should
714	   (string-equal (file-remote-p "/-:[]:" 'method) "default-method"))
715	  (should (string-equal (file-remote-p "/-:[]:" 'user) "default-user"))
716	  (should (string-equal (file-remote-p "/-:[]:" 'host) "default-host"))
717	  (should (string-equal (file-remote-p "/-:[]:" 'localname) ""))
718	  (should (string-equal (file-remote-p "/-:[]:" 'hop) nil))
719
720	  ;; Expand `tramp-default-method' and `tramp-default-user'.
721	  (let ((tramp-default-host "::1"))
722	    (should
723	     (string-equal
724	      (file-remote-p "/-:[]:")
725	      (format "/%s:%s@%s:" "default-method" "default-user" "[::1]")))
726	    (should
727	     (string-equal (file-remote-p "/-:[]:" 'method) "default-method"))
728	    (should
729	     (string-equal (file-remote-p "/-:[]:" 'user) "default-user"))
730	    (should (string-equal (file-remote-p "/-:[]:" 'host) "::1"))
731	    (should (string-equal (file-remote-p "/-:[]:" 'localname) ""))
732	    (should (string-equal (file-remote-p "/-:[]:" 'hop) nil)))
733
734	  ;; Expand `tramp-default-method' and `tramp-default-user'.
735	  (should
736	   (string-equal
737	    (file-remote-p "/-:[::1]:")
738	    (format "/%s:%s@%s:" "default-method" "default-user" "[::1]")))
739	  (should
740	   (string-equal (file-remote-p "/-:[::1]:" 'method) "default-method"))
741	  (should
742	   (string-equal (file-remote-p "/-:[::1]:" 'user) "default-user"))
743	  (should (string-equal (file-remote-p "/-:[::1]:" 'host) "::1"))
744	  (should (string-equal (file-remote-p "/-:[::1]:" 'localname) ""))
745	  (should (string-equal (file-remote-p "/-:[::1]:" 'hop) nil))
746
747	  ;; Expand `tramp-default-method'.
748	  (should (string-equal
749		   (file-remote-p "/-:user@[::1]:")
750		   (format "/%s:%s@%s:" "default-method" "user" "[::1]")))
751	  (should (string-equal
752		   (file-remote-p "/-:user@[::1]:" 'method) "default-method"))
753	  (should (string-equal (file-remote-p "/-:user@[::1]:" 'user) "user"))
754	  (should (string-equal (file-remote-p "/-:user@[::1]:" 'host) "::1"))
755	  (should (string-equal (file-remote-p "/-:user@[::1]:" 'localname) ""))
756	  (should (string-equal (file-remote-p "/-:user@[::1]:" 'hop) nil))
757
758	  ;; Expand `tramp-default-user'.
759	  (should (string-equal
760		   (file-remote-p "/method:[::1]:")
761		   (format "/%s:%s@%s:" "method" "default-user" "[::1]")))
762	  (should
763	   (string-equal (file-remote-p "/method:[::1]:" 'method) "method"))
764	  (should (string-equal
765		   (file-remote-p "/method:[::1]:" 'user) "default-user"))
766	  (should (string-equal (file-remote-p "/method:[::1]:" 'host) "::1"))
767	  (should (string-equal (file-remote-p "/method:[::1]:" 'localname) ""))
768	  (should (string-equal (file-remote-p "/method:[::1]:" 'hop) nil))
769
770	  ;; No expansion.
771	  (should (string-equal
772		   (file-remote-p "/method:user@[::1]:")
773		   (format "/%s:%s@%s:" "method" "user" "[::1]")))
774	  (should (string-equal
775		   (file-remote-p "/method:user@[::1]:" 'method) "method"))
776	  (should
777	   (string-equal (file-remote-p "/method:user@[::1]:" 'user) "user"))
778	  (should
779	   (string-equal (file-remote-p "/method:user@[::1]:" 'host) "::1"))
780	  (should (string-equal
781		   (file-remote-p "/method:user@[::1]:" 'localname) ""))
782	  (should (string-equal (file-remote-p "/method:user@[::1]:" 'hop) nil))
783
784	  ;; Local file name part.
785	  (should (string-equal (file-remote-p "/-:host:/:" 'localname) "/:"))
786	  (should (string-equal (file-remote-p "/method:::" 'localname) ":"))
787	  (should (string-equal (file-remote-p "/method:: " 'localname) " "))
788	  (should
789	   (string-equal (file-remote-p "/method::file" 'localname) "file"))
790	  (should (string-equal
791		   (file-remote-p "/method::/path/to/file" 'localname)
792		   "/path/to/file"))
793
794	  ;; Multihop.
795	  (should
796	   (string-equal
797	    (file-remote-p
798	     "/method1:user1@host1|method2:user2@host2:/path/to/file")
799	    (format "/%s:%s@%s|%s:%s@%s:"
800		    "method1" "user1" "host1" "method2" "user2" "host2")))
801	  (should
802	   (string-equal
803	    (file-remote-p
804	     "/method1:user1@host1|method2:user2@host2:/path/to/file" 'method)
805	    "method2"))
806	  (should
807	   (string-equal
808	    (file-remote-p
809	     "/method1:user1@host1|method2:user2@host2:/path/to/file" 'user)
810	    "user2"))
811	  (should
812	   (string-equal
813	    (file-remote-p
814	     "/method1:user1@host1|method2:user2@host2:/path/to/file" 'host)
815	    "host2"))
816	  (should
817	   (string-equal
818	    (file-remote-p
819	     "/method1:user1@host1|method2:user2@host2:/path/to/file"
820	     'localname)
821	    "/path/to/file"))
822	  (should
823	   (string-equal
824	    (file-remote-p
825	     "/method1:user1@host1|method2:user2@host2:/path/to/file" 'hop)
826	    (format "%s:%s@%s|"
827		    "method1" "user1" "host1")))
828
829	  (should
830	   (string-equal
831	    (file-remote-p
832	     (concat
833	      "/method1:user1@host1"
834	      "|method2:user2@host2"
835	      "|method3:user3@host3:/path/to/file"))
836	    (format "/%s:%s@%s|%s:%s@%s|%s:%s@%s:"
837		    "method1" "user1" "host1"
838		    "method2" "user2" "host2"
839		    "method3" "user3" "host3")))
840	  (should
841	   (string-equal
842	    (file-remote-p
843	     (concat
844	      "/method1:user1@host1"
845	      "|method2:user2@host2"
846	      "|method3:user3@host3:/path/to/file")
847	     'method)
848	    "method3"))
849	  (should
850	   (string-equal
851	    (file-remote-p
852	     (concat
853	      "/method1:user1@host1"
854	      "|method2:user2@host2"
855	      "|method3:user3@host3:/path/to/file")
856	     'user)
857	    "user3"))
858	  (should
859	   (string-equal
860	    (file-remote-p
861	     (concat
862	      "/method1:user1@host1"
863	      "|method2:user2@host2"
864	      "|method3:user3@host3:/path/to/file")
865	     'host)
866	    "host3"))
867	  (should
868	   (string-equal
869	    (file-remote-p
870	     (concat
871	      "/method1:user1@host1"
872	      "|method2:user2@host2"
873	      "|method3:user3@host3:/path/to/file")
874	     'localname)
875	    "/path/to/file"))
876	  (should
877	   (string-equal
878	    (file-remote-p
879	     (concat
880	      "/method1:user1@host1"
881	      "|method2:user2@host2"
882	      "|method3:user3@host3:/path/to/file")
883	     'hop)
884	    (format "%s:%s@%s|%s:%s@%s|"
885		    "method1" "user1" "host1" "method2" "user2" "host2")))
886
887	  ;; Expand `tramp-default-method-alist'.
888	  (add-to-list 'tramp-default-method-alist '("host1" "user1" "method1"))
889	  (add-to-list 'tramp-default-method-alist '("host2" "user2" "method2"))
890	  (add-to-list 'tramp-default-method-alist '("host3" "user3" "method3"))
891	  (should
892	   (string-equal
893	    (file-remote-p
894	     (concat
895	      "/-:user1@host1"
896	      "|-:user2@host2"
897	      "|-:user3@host3:/path/to/file"))
898	    (format "/%s:%s@%s|%s:%s@%s|%s:%s@%s:"
899		    "method1" "user1" "host1"
900		    "method2" "user2" "host2"
901		    "method3" "user3" "host3")))
902
903	  ;; Expand `tramp-default-user-alist'.
904	  (add-to-list 'tramp-default-user-alist '("method1" "host1" "user1"))
905	  (add-to-list 'tramp-default-user-alist '("method2" "host2" "user2"))
906	  (add-to-list 'tramp-default-user-alist '("method3" "host3" "user3"))
907	  (should
908	   (string-equal
909	    (file-remote-p
910	     (concat
911	      "/method1:host1"
912	      "|method2:host2"
913	      "|method3:host3:/path/to/file"))
914	    (format "/%s:%s@%s|%s:%s@%s|%s:%s@%s:"
915		    "method1" "user1" "host1"
916		    "method2" "user2" "host2"
917		    "method3" "user3" "host3")))
918
919	  ;; Expand `tramp-default-host-alist'.
920	  (add-to-list 'tramp-default-host-alist '("method1" "user1" "host1"))
921	  (add-to-list 'tramp-default-host-alist '("method2" "user2" "host2"))
922	  (add-to-list 'tramp-default-host-alist '("method3" "user3" "host3"))
923	  (should
924	   (string-equal
925	    (file-remote-p
926	     (concat
927	      "/method1:user1@"
928	      "|method2:user2@"
929	      "|method3:user3@:/path/to/file"))
930	    (format "/%s:%s@%s|%s:%s@%s|%s:%s@%s:"
931		    "method1" "user1" "host1"
932		    "method2" "user2" "host2"
933		    "method3" "user3" "host3")))
934
935	  ;; Ad-hoc user name and host name expansion.
936	  (setq tramp-default-method-alist nil
937		tramp-default-user-alist nil
938		tramp-default-host-alist nil)
939	  (should
940	   (string-equal
941	    (file-remote-p
942	     (concat
943	      "/method1:user1@host1"
944	      "|method2:user2@"
945	      "|method3:user3@:/path/to/file"))
946	    (format "/%s:%s@%s|%s:%s@%s|%s:%s@%s:"
947		    "method1" "user1" "host1"
948		    "method2" "user2" "host1"
949		    "method3" "user3" "host1")))
950	  (should
951	   (string-equal
952	    (file-remote-p
953	     (concat
954	      "/method1:%u@%h"
955	      "|method2:user2@host2"
956	      "|method3:%u@%h"
957	      "|method4:user4%domain4@host4#1234:/path/to/file"))
958	    (format "/%s:%s@%s|%s:%s@%s|%s:%s@%s|%s:%s@%s:"
959		    "method1" "user2" "host2"
960		    "method2" "user2" "host2"
961		    "method3" "user4" "host4"
962		    "method4" "user4%domain4" "host4#1234"))))
963
964      ;; Exit.
965      (tramp-change-syntax syntax))))
966
967(ert-deftest tramp-test02-file-name-dissect-simplified ()
968  "Check simplified file name components."
969  :tags '(:expensive-test)
970  (let ((tramp-default-method "default-method")
971	(tramp-default-user "default-user")
972	(tramp-default-host "default-host")
973	tramp-default-user-alist
974	tramp-default-host-alist
975	;; Suppress method name check.
976	(non-essential t)
977	;; Suppress check for multihops.
978	(tramp-cache-data (make-hash-table :test #'equal))
979	(tramp-connection-properties '((nil "login-program" t)))
980	(syntax tramp-syntax))
981    (unwind-protect
982	(progn
983	  (tramp-change-syntax 'simplified)
984	  ;; An unknown default method shall raise an error.
985	  (let (non-essential)
986	    (should-error
987	     (expand-file-name "/user@host:")
988	     :type 'user-error))
989
990	  ;; Expand `tramp-default-method' and `tramp-default-user'.
991	  (should (string-equal
992		   (file-remote-p "/host:")
993		   (format "/%s@%s:" "default-user" "host")))
994	  (should (string-equal
995		   (file-remote-p "/host:" 'method) "default-method"))
996	  (should (string-equal (file-remote-p "/host:" 'user) "default-user"))
997	  (should (string-equal (file-remote-p "/host:" 'host) "host"))
998	  (should (string-equal (file-remote-p "/host:" 'localname) ""))
999	  (should (string-equal (file-remote-p "/host:" 'hop) nil))
1000
1001	  ;; Expand `tramp-default-method' and `tramp-default-host'.
1002	  (should (string-equal
1003		   (file-remote-p "/user@:")
1004		   (format "/%s@%s:" "user" "default-host")))
1005	  (should (string-equal
1006		   (file-remote-p "/user@:" 'method) "default-method"))
1007	  (should (string-equal (file-remote-p "/user@:" 'user) "user"))
1008	  (should (string-equal (file-remote-p "/user@:" 'host) "default-host"))
1009	  (should (string-equal (file-remote-p "/user@:" 'localname) ""))
1010	  (should (string-equal (file-remote-p "/user@:" 'hop) nil))
1011
1012	  ;; Expand `tramp-default-method'.
1013	  (should (string-equal
1014		   (file-remote-p "/user@host:")
1015		   (format "/%s@%s:" "user" "host")))
1016	  (should (string-equal
1017		   (file-remote-p "/user@host:" 'method) "default-method"))
1018	  (should (string-equal (file-remote-p "/user@host:" 'user) "user"))
1019	  (should (string-equal (file-remote-p "/user@host:" 'host) "host"))
1020	  (should (string-equal (file-remote-p "/user@host:" 'localname) ""))
1021	  (should (string-equal (file-remote-p "/user@host:" 'hop) nil))
1022
1023	  ;; No expansion.
1024	  (should (string-equal
1025		   (file-remote-p "/user@email@host:")
1026		   (format "/%s@%s:" "user@email" "host")))
1027	  (should (string-equal
1028		   (file-remote-p
1029		    "/user@email@host:" 'method) "default-method"))
1030	  (should (string-equal
1031		   (file-remote-p "/user@email@host:" 'user) "user@email"))
1032	  (should (string-equal
1033		   (file-remote-p "/user@email@host:" 'host) "host"))
1034	  (should (string-equal
1035		   (file-remote-p "/user@email@host:" 'localname) ""))
1036	  (should (string-equal
1037		   (file-remote-p "/user@email@host:" 'hop) nil))
1038
1039	  ;; Expand `tramp-default-method' and `tramp-default-user'.
1040	  (should (string-equal
1041		   (file-remote-p "/host#1234:")
1042		   (format "/%s@%s:" "default-user" "host#1234")))
1043	  (should (string-equal
1044		   (file-remote-p "/host#1234:" 'method) "default-method"))
1045	  (should (string-equal
1046		   (file-remote-p "/host#1234:" 'user) "default-user"))
1047	  (should (string-equal
1048		   (file-remote-p "/host#1234:" 'host) "host#1234"))
1049	  (should (string-equal (file-remote-p "/host#1234:" 'localname) ""))
1050	  (should (string-equal (file-remote-p "/host#1234:" 'hop) nil))
1051
1052	  ;; Expand `tramp-default-method'.
1053	  (should (string-equal
1054		   (file-remote-p "/user@host#1234:")
1055		   (format "/%s@%s:" "user" "host#1234")))
1056	  (should (string-equal
1057		   (file-remote-p "/user@host#1234:" 'method) "default-method"))
1058	  (should (string-equal
1059		   (file-remote-p "/user@host#1234:" 'user) "user"))
1060	  (should (string-equal
1061		   (file-remote-p "/user@host#1234:" 'host) "host#1234"))
1062	  (should (string-equal
1063		   (file-remote-p "/user@host#1234:" 'localname) ""))
1064	  (should (string-equal (file-remote-p "/user@host#1234:" 'hop) nil))
1065
1066	  ;; Expand `tramp-default-method' and `tramp-default-user'.
1067	  (should (string-equal
1068		   (file-remote-p "/1.2.3.4:")
1069		   (format "/%s@%s:" "default-user" "1.2.3.4")))
1070	  (should (string-equal
1071		   (file-remote-p "/1.2.3.4:" 'method) "default-method"))
1072	  (should (string-equal
1073		   (file-remote-p "/1.2.3.4:" 'user) "default-user"))
1074	  (should (string-equal (file-remote-p "/1.2.3.4:" 'host) "1.2.3.4"))
1075	  (should (string-equal (file-remote-p "/1.2.3.4:" 'localname) ""))
1076	  (should (string-equal (file-remote-p "/1.2.3.4:" 'hop) nil))
1077
1078	  ;; Expand `tramp-default-method'.
1079	  (should (string-equal
1080		   (file-remote-p "/user@1.2.3.4:")
1081		   (format "/%s@%s:" "user" "1.2.3.4")))
1082	  (should (string-equal
1083		   (file-remote-p "/user@1.2.3.4:" 'method) "default-method"))
1084	  (should (string-equal (file-remote-p "/user@1.2.3.4:" 'user) "user"))
1085	  (should (string-equal
1086		   (file-remote-p "/user@1.2.3.4:" 'host) "1.2.3.4"))
1087	  (should (string-equal (file-remote-p "/user@1.2.3.4:" 'localname) ""))
1088	  (should (string-equal (file-remote-p "/user@1.2.3.4:" 'hop) nil))
1089
1090	  ;; Expand `tramp-default-method', `tramp-default-user' and
1091	  ;; `tramp-default-host'.
1092	  (should (string-equal
1093		   (file-remote-p "/[]:")
1094		   (format
1095		    "/%s@%s:" "default-user" "default-host")))
1096	  (should (string-equal
1097		   (file-remote-p "/[]:" 'method) "default-method"))
1098	  (should (string-equal (file-remote-p "/[]:" 'user) "default-user"))
1099	  (should (string-equal (file-remote-p "/[]:" 'host) "default-host"))
1100	  (should (string-equal (file-remote-p "/[]:" 'localname) ""))
1101	  (should (string-equal (file-remote-p "/[]:" 'hop) nil))
1102
1103	  ;; Expand `tramp-default-method' and `tramp-default-user'.
1104	  (let ((tramp-default-host "::1"))
1105	    (should (string-equal
1106		     (file-remote-p "/[]:")
1107		     (format "/%s@%s:" "default-user" "[::1]")))
1108	    (should (string-equal
1109		     (file-remote-p "/[]:" 'method) "default-method"))
1110	    (should (string-equal (file-remote-p "/[]:" 'user) "default-user"))
1111	    (should (string-equal (file-remote-p "/[]:" 'host) "::1"))
1112	    (should (string-equal (file-remote-p "/[]:" 'localname) ""))
1113	    (should (string-equal (file-remote-p "/[]:" 'hop) nil)))
1114
1115	  ;; Expand `tramp-default-method' and `tramp-default-user'.
1116	  (should (string-equal
1117		   (file-remote-p "/[::1]:")
1118		   (format "/%s@%s:" "default-user" "[::1]")))
1119	  (should (string-equal
1120		   (file-remote-p "/[::1]:" 'method) "default-method"))
1121	  (should (string-equal (file-remote-p "/[::1]:" 'user) "default-user"))
1122	  (should (string-equal (file-remote-p "/[::1]:" 'host) "::1"))
1123	  (should (string-equal (file-remote-p "/[::1]:" 'localname) ""))
1124	  (should (string-equal (file-remote-p "/[::1]:" 'hop) nil))
1125
1126	  ;; Expand `tramp-default-method'.
1127	  (should (string-equal
1128		   (file-remote-p "/user@[::1]:")
1129		   (format "/%s@%s:" "user" "[::1]")))
1130	  (should (string-equal
1131		   (file-remote-p "/user@[::1]:" 'method) "default-method"))
1132	  (should (string-equal (file-remote-p "/user@[::1]:" 'user) "user"))
1133	  (should (string-equal (file-remote-p "/user@[::1]:" 'host) "::1"))
1134	  (should (string-equal (file-remote-p "/user@[::1]:" 'localname) ""))
1135	  (should (string-equal (file-remote-p "/user@[::1]:" 'hop) nil))
1136
1137	  ;; Local file name part.
1138	  (should (string-equal (file-remote-p "/host:/:" 'localname) "/:"))
1139	  (should (string-equal (file-remote-p "/host::" 'localname) ":"))
1140	  (should (string-equal (file-remote-p "/host: " 'localname) " "))
1141	  (should (string-equal (file-remote-p "/host:file" 'localname) "file"))
1142	  (should (string-equal
1143		   (file-remote-p "/host:/path/to/file" 'localname)
1144		   "/path/to/file"))
1145
1146	  ;; Multihop.
1147	  (should
1148	   (string-equal
1149	    (file-remote-p "/user1@host1|user2@host2:/path/to/file")
1150	    (format "/%s@%s|%s@%s:" "user1" "host1" "user2" "host2")))
1151	  (should
1152	   (string-equal
1153	    (file-remote-p
1154	     "/user1@host1|user2@host2:/path/to/file" 'method)
1155	    "default-method"))
1156	  (should
1157	   (string-equal
1158	    (file-remote-p
1159	     "/user1@host1|user2@host2:/path/to/file" 'user)
1160	    "user2"))
1161	  (should
1162	   (string-equal
1163	    (file-remote-p
1164	     "/user1@host1|user2@host2:/path/to/file" 'host)
1165	    "host2"))
1166	  (should
1167	   (string-equal
1168	    (file-remote-p
1169	     "/user1@host1|user2@host2:/path/to/file" 'localname)
1170	    "/path/to/file"))
1171	  (should
1172	   (string-equal
1173	    (file-remote-p
1174	     "/user1@host1|user2@host2:/path/to/file" 'hop)
1175	    (format "%s@%s|" "user1" "host1")))
1176
1177	  (should
1178	   (string-equal
1179	    (file-remote-p
1180	     (concat
1181	      "/user1@host1"
1182	      "|user2@host2"
1183	      "|user3@host3:/path/to/file"))
1184	    (format "/%s@%s|%s@%s|%s@%s:"
1185		    "user1" "host1"
1186		    "user2" "host2"
1187		    "user3" "host3")))
1188	  (should
1189	   (string-equal
1190	    (file-remote-p
1191	     (concat
1192	      "/user1@host1"
1193	      "|user2@host2"
1194	      "|user3@host3:/path/to/file")
1195	     'method)
1196	    "default-method"))
1197	  (should
1198	   (string-equal
1199	    (file-remote-p
1200	     (concat
1201	      "/user1@host1"
1202	      "|user2@host2"
1203	      "|user3@host3:/path/to/file")
1204	     'user)
1205	    "user3"))
1206	  (should
1207	   (string-equal
1208	    (file-remote-p
1209	     (concat
1210	      "/user1@host1"
1211	      "|user2@host2"
1212	      "|user3@host3:/path/to/file")
1213	     'host)
1214	    "host3"))
1215	  (should
1216	   (string-equal
1217	    (file-remote-p
1218	     (concat
1219	      "/user1@host1"
1220	      "|user2@host2"
1221	      "|user3@host3:/path/to/file")
1222	     'localname)
1223	    "/path/to/file"))
1224	  (should
1225	   (string-equal
1226	    (file-remote-p
1227	     (concat
1228	      "/user1@host1"
1229	      "|user2@host2"
1230	      "|user3@host3:/path/to/file")
1231	     'hop)
1232	    (format "%s@%s|%s@%s|"
1233		    "user1" "host1" "user2" "host2")))
1234
1235	  ;; Expand `tramp-default-user-alist'.
1236	  (add-to-list 'tramp-default-user-alist '(nil "host1" "user1"))
1237	  (add-to-list 'tramp-default-user-alist '(nil "host2" "user2"))
1238	  (add-to-list 'tramp-default-user-alist '(nil "host3" "user3"))
1239	  (should
1240	   (string-equal
1241	    (file-remote-p
1242	     (concat
1243	      "/host1"
1244	      "|host2"
1245	      "|host3:/path/to/file"))
1246	    (format "/%s@%s|%s@%s|%s@%s:"
1247		    "user1" "host1"
1248		    "user2" "host2"
1249		    "user3" "host3")))
1250
1251	  ;; Expand `tramp-default-host-alist'.
1252	  (add-to-list 'tramp-default-host-alist '(nil "user1" "host1"))
1253	  (add-to-list 'tramp-default-host-alist '(nil "user2" "host2"))
1254	  (add-to-list 'tramp-default-host-alist '(nil "user3" "host3"))
1255	  (should
1256	   (string-equal
1257	    (file-remote-p
1258	     (concat
1259	      "/user1@"
1260	      "|user2@"
1261	      "|user3@:/path/to/file"))
1262	    (format "/%s@%s|%s@%s|%s@%s:"
1263		    "user1" "host1"
1264		    "user2" "host2"
1265		    "user3" "host3")))
1266
1267	  ;; Ad-hoc user name and host name expansion.
1268	  (setq tramp-default-user-alist nil
1269		tramp-default-host-alist nil)
1270	  (should
1271	   (string-equal
1272	    (file-remote-p
1273	     (concat
1274	      "/user1@host1"
1275	      "|user2@"
1276	      "|user3@:/path/to/file"))
1277	    (format "/%s@%s|%s@%s|%s@%s:"
1278		    "user1" "host1"
1279		    "user2" "host1"
1280		    "user3" "host1")))
1281	  (should
1282	   (string-equal
1283	    (file-remote-p
1284	     (concat
1285	      "/%u@%h"
1286	      "|user2@host2"
1287	      "|%u@%h"
1288	      "|user4%domain4@host4#1234:/path/to/file"))
1289	    (format "/%s@%s|%s@%s|%s@%s|%s@%s:"
1290		    "user2" "host2"
1291		    "user2" "host2"
1292		    "user4" "host4"
1293		    "user4%domain4" "host4#1234"))))
1294
1295      ;; Exit.
1296      (tramp-change-syntax syntax))))
1297
1298(ert-deftest tramp-test02-file-name-dissect-separate ()
1299  "Check separate file name components."
1300  :tags '(:expensive-test)
1301  (let ((tramp-default-method "default-method")
1302	(tramp-default-user "default-user")
1303	(tramp-default-host "default-host")
1304	tramp-default-method-alist
1305	tramp-default-user-alist
1306	tramp-default-host-alist
1307	;; Suppress method name check.
1308	(non-essential t)
1309	;; Suppress check for multihops.
1310	(tramp-cache-data (make-hash-table :test #'equal))
1311	(tramp-connection-properties '((nil "login-program" t)))
1312	(syntax tramp-syntax))
1313    (unwind-protect
1314	(progn
1315	  (tramp-change-syntax 'separate)
1316	  ;; An unknown method shall raise an error.
1317	  (let (non-essential)
1318	    (should-error
1319	     (expand-file-name "/[method/user@host]")
1320	     :type 'user-error))
1321
1322	  ;; Expand `tramp-default-user' and `tramp-default-host'.
1323	  (should (string-equal
1324		   (file-remote-p "/[method/]")
1325		   (format
1326		    "/[%s/%s@%s]" "method" "default-user" "default-host")))
1327	  (should (string-equal (file-remote-p "/[method/]" 'method) "method"))
1328	  (should (string-equal
1329		   (file-remote-p "/[method/]" 'user) "default-user"))
1330	  (should (string-equal
1331		   (file-remote-p "/[method/]" 'host) "default-host"))
1332	  (should (string-equal (file-remote-p "/[method/]" 'localname) ""))
1333	  (should (string-equal (file-remote-p "/[method/]" 'hop) nil))
1334
1335	  ;; Expand `tramp-default-method' and `tramp-default-user'.
1336	  (should (string-equal
1337		   (file-remote-p "/[/host]")
1338		   (format
1339		    "/[%s/%s@%s]" "default-method" "default-user" "host")))
1340	  (should (string-equal
1341		   (file-remote-p "/[/host]" 'method) "default-method"))
1342	  (should (string-equal
1343		   (file-remote-p "/[/host]" 'user) "default-user"))
1344	  (should (string-equal (file-remote-p "/[/host]" 'host) "host"))
1345	  (should (string-equal (file-remote-p "/[/host]" 'localname) ""))
1346	  (should (string-equal (file-remote-p "/[/host]" 'hop) nil))
1347
1348	  ;; Expand `tramp-default-method' and `tramp-default-host'.
1349	  (should (string-equal
1350		   (file-remote-p "/[/user@]")
1351		   (format
1352		    "/[%s/%s@%s]" "default-method" "user" "default-host")))
1353	  (should (string-equal
1354		   (file-remote-p "/[/user@]" 'method) "default-method"))
1355	  (should (string-equal (file-remote-p "/[/user@]" 'user) "user"))
1356	  (should (string-equal
1357		   (file-remote-p "/[/user@]" 'host) "default-host"))
1358	  (should (string-equal (file-remote-p "/[/user@]" 'localname) ""))
1359	  (should (string-equal (file-remote-p "/[/user@]" 'hop) nil))
1360
1361	  ;; Expand `tramp-default-method'.
1362	  (should (string-equal
1363		   (file-remote-p "/[/user@host]")
1364		   (format "/[%s/%s@%s]" "default-method" "user" "host")))
1365	  (should (string-equal
1366		   (file-remote-p "/[/user@host]" 'method) "default-method"))
1367	  (should (string-equal (file-remote-p "/[/user@host]" 'user) "user"))
1368	  (should (string-equal (file-remote-p "/[/user@host]" 'host) "host"))
1369	  (should (string-equal (file-remote-p "/[/user@host]" 'localname) ""))
1370	  (should (string-equal (file-remote-p "/[/user@host]" 'hop) nil))
1371
1372	  ;; Expand `tramp-default-method' and `tramp-default-user'.
1373	  (should (string-equal
1374		   (file-remote-p "/[-/host]")
1375		   (format
1376		    "/[%s/%s@%s]" "default-method" "default-user" "host")))
1377	  (should (string-equal
1378		   (file-remote-p "/[-/host]" 'method) "default-method"))
1379	  (should (string-equal
1380		   (file-remote-p "/[-/host]" 'user) "default-user"))
1381	  (should (string-equal (file-remote-p "/[-/host]" 'host) "host"))
1382	  (should (string-equal (file-remote-p "/[-/host]" 'localname) ""))
1383	  (should (string-equal (file-remote-p "/[-/host]" 'hop) nil))
1384
1385	  ;; Expand `tramp-default-method' and `tramp-default-host'.
1386	  (should (string-equal
1387		   (file-remote-p "/[-/user@]")
1388		   (format
1389		    "/[%s/%s@%s]" "default-method" "user" "default-host")))
1390	  (should (string-equal
1391		   (file-remote-p "/[-/user@]" 'method) "default-method"))
1392	  (should (string-equal (file-remote-p "/[-/user@]" 'user) "user"))
1393	  (should (string-equal
1394		   (file-remote-p "/[-/user@]" 'host) "default-host"))
1395	  (should (string-equal (file-remote-p "/[-/user@]" 'localname) ""))
1396	  (should (string-equal (file-remote-p "/[-/user@]" 'hop) nil))
1397
1398	  ;; Expand `tramp-default-method'.
1399	  (should (string-equal
1400		   (file-remote-p "/[-/user@host]")
1401		   (format "/[%s/%s@%s]" "default-method" "user" "host")))
1402	  (should (string-equal
1403		   (file-remote-p "/[-/user@host]" 'method) "default-method"))
1404	  (should (string-equal (file-remote-p "/[-/user@host]" 'user) "user"))
1405	  (should (string-equal (file-remote-p "/[-/user@host]" 'host) "host"))
1406	  (should (string-equal (file-remote-p "/[-/user@host]" 'localname) ""))
1407	  (should (string-equal (file-remote-p "/[-/user@host]" 'hop) nil))
1408
1409	  ;; Expand `tramp-default-user'.
1410	  (should (string-equal
1411		   (file-remote-p "/[method/host]")
1412		   (format "/[%s/%s@%s]" "method" "default-user" "host")))
1413	  (should (string-equal
1414		   (file-remote-p "/[method/host]" 'method) "method"))
1415	  (should (string-equal
1416		   (file-remote-p "/[method/host]" 'user) "default-user"))
1417	  (should (string-equal (file-remote-p "/[method/host]" 'host) "host"))
1418	  (should (string-equal (file-remote-p "/[method/host]" 'localname) ""))
1419	  (should (string-equal (file-remote-p "/[method/host]" 'hop) nil))
1420
1421	  ;; Expand `tramp-default-host'.
1422	  (should (string-equal
1423		   (file-remote-p "/[method/user@]")
1424		   (format "/[%s/%s@%s]" "method" "user" "default-host")))
1425	  (should (string-equal
1426		   (file-remote-p "/[method/user@]" 'method) "method"))
1427	  (should (string-equal (file-remote-p "/[method/user@]" 'user) "user"))
1428	  (should (string-equal
1429		   (file-remote-p "/[method/user@]" 'host) "default-host"))
1430	  (should (string-equal
1431		   (file-remote-p "/[method/user@]" 'localname) ""))
1432	  (should (string-equal (file-remote-p "/[method/user@]" 'hop) nil))
1433
1434	  ;; No expansion.
1435	  (should (string-equal
1436		   (file-remote-p "/[method/user@host]")
1437		   (format "/[%s/%s@%s]" "method" "user" "host")))
1438	  (should (string-equal
1439		   (file-remote-p "/[method/user@host]" 'method) "method"))
1440	  (should (string-equal
1441		   (file-remote-p "/[method/user@host]" 'user) "user"))
1442	  (should (string-equal
1443		   (file-remote-p "/[method/user@host]" 'host) "host"))
1444	  (should (string-equal
1445		   (file-remote-p "/[method/user@host]" 'localname) ""))
1446	  (should (string-equal
1447		   (file-remote-p "/[method/user@host]" 'hop) nil))
1448
1449	  ;; No expansion.
1450	  (should (string-equal
1451		   (file-remote-p "/[method/user@email@host]")
1452		   (format "/[%s/%s@%s]" "method" "user@email" "host")))
1453	  (should (string-equal
1454		   (file-remote-p
1455		    "/[method/user@email@host]" 'method) "method"))
1456	  (should (string-equal
1457		   (file-remote-p
1458		    "/[method/user@email@host]" 'user) "user@email"))
1459	  (should (string-equal
1460		   (file-remote-p "/[method/user@email@host]" 'host) "host"))
1461	  (should (string-equal
1462		   (file-remote-p "/[method/user@email@host]" 'localname) ""))
1463	  (should (string-equal
1464		   (file-remote-p "/[method/user@email@host]" 'hop) nil))
1465
1466	  ;; Expand `tramp-default-method' and `tramp-default-user'.
1467	  (should (string-equal
1468		   (file-remote-p "/[/host#1234]")
1469		   (format
1470		    "/[%s/%s@%s]" "default-method" "default-user" "host#1234")))
1471	  (should (string-equal
1472		   (file-remote-p "/[/host#1234]" 'method) "default-method"))
1473	  (should (string-equal
1474		   (file-remote-p "/[/host#1234]" 'user) "default-user"))
1475	  (should (string-equal
1476		   (file-remote-p "/[/host#1234]" 'host) "host#1234"))
1477	  (should (string-equal (file-remote-p "/[/host#1234]" 'localname) ""))
1478	  (should (string-equal (file-remote-p "/[/host#1234]" 'hop) nil))
1479
1480	  ;; Expand `tramp-default-method'.
1481	  (should (string-equal
1482		   (file-remote-p "/[/user@host#1234]")
1483		   (format "/[%s/%s@%s]" "default-method" "user" "host#1234")))
1484	  (should (string-equal
1485		   (file-remote-p
1486		    "/[/user@host#1234]" 'method) "default-method"))
1487	  (should (string-equal
1488		   (file-remote-p
1489		    "/[/user@host#1234]" 'user) "user"))
1490	  (should (string-equal
1491		   (file-remote-p "/[/user@host#1234]" 'host) "host#1234"))
1492	  (should (string-equal
1493		   (file-remote-p "/[/user@host#1234]" 'localname) ""))
1494	  (should (string-equal (file-remote-p "/[/user@host#1234]" 'hop) nil))
1495
1496	  ;; Expand `tramp-default-method' and `tramp-default-user'.
1497	  (should (string-equal
1498		   (file-remote-p "/[-/host#1234]")
1499		   (format
1500		    "/[%s/%s@%s]" "default-method" "default-user" "host#1234")))
1501	  (should (string-equal
1502		   (file-remote-p "/[-/host#1234]" 'method) "default-method"))
1503	  (should (string-equal
1504		   (file-remote-p "/[-/host#1234]" 'user) "default-user"))
1505	  (should (string-equal
1506		   (file-remote-p "/[-/host#1234]" 'host) "host#1234"))
1507	  (should (string-equal (file-remote-p "/[-/host#1234]" 'localname) ""))
1508	  (should (string-equal (file-remote-p "/[-/host#1234]" 'hop) nil))
1509
1510	  ;; Expand `tramp-default-method'.
1511	  (should (string-equal
1512		   (file-remote-p "/[-/user@host#1234]")
1513		   (format "/[%s/%s@%s]" "default-method" "user" "host#1234")))
1514	  (should (string-equal
1515		   (file-remote-p
1516		    "/[-/user@host#1234]" 'method) "default-method"))
1517	  (should (string-equal
1518		   (file-remote-p
1519		    "/[-/user@host#1234]" 'user) "user"))
1520	  (should (string-equal
1521		   (file-remote-p "/[-/user@host#1234]" 'host) "host#1234"))
1522	  (should (string-equal
1523		   (file-remote-p "/[-/user@host#1234]" 'localname) ""))
1524	  (should (string-equal (file-remote-p "/[-/user@host#1234]" 'hop) nil))
1525
1526	  ;; Expand `tramp-default-user'.
1527	  (should (string-equal
1528		   (file-remote-p "/[method/host#1234]")
1529		   (format "/[%s/%s@%s]" "method" "default-user" "host#1234")))
1530	  (should (string-equal
1531		   (file-remote-p "/[method/host#1234]" 'method) "method"))
1532	  (should (string-equal
1533		   (file-remote-p "/[method/host#1234]" 'user) "default-user"))
1534	  (should (string-equal
1535		   (file-remote-p "/[method/host#1234]" 'host) "host#1234"))
1536	  (should (string-equal
1537		   (file-remote-p "/[method/host#1234]" 'localname) ""))
1538	  (should (string-equal (file-remote-p "/[method/host#1234]" 'hop) nil))
1539
1540	  ;; No expansion.
1541	  (should (string-equal
1542		   (file-remote-p "/[method/user@host#1234]")
1543		   (format "/[%s/%s@%s]" "method" "user" "host#1234")))
1544	  (should (string-equal
1545		   (file-remote-p "/[method/user@host#1234]" 'method) "method"))
1546	  (should (string-equal
1547		   (file-remote-p "/[method/user@host#1234]" 'user) "user"))
1548	  (should (string-equal
1549		   (file-remote-p
1550		    "/[method/user@host#1234]" 'host) "host#1234"))
1551	  (should (string-equal
1552		   (file-remote-p "/[method/user@host#1234]" 'localname) ""))
1553	  (should (string-equal
1554		   (file-remote-p "/[method/user@host#1234]" 'hop) nil))
1555
1556	  ;; Expand `tramp-default-method' and `tramp-default-user'.
1557	  (should (string-equal
1558		   (file-remote-p "/[/1.2.3.4]")
1559		   (format
1560		    "/[%s/%s@%s]" "default-method" "default-user" "1.2.3.4")))
1561	  (should (string-equal
1562		   (file-remote-p "/[/1.2.3.4]" 'method) "default-method"))
1563	  (should (string-equal
1564		   (file-remote-p "/[/1.2.3.4]" 'user) "default-user"))
1565	  (should (string-equal
1566		   (file-remote-p "/[/1.2.3.4]" 'host) "1.2.3.4"))
1567	  (should (string-equal (file-remote-p "/[/1.2.3.4]" 'localname) ""))
1568	  (should (string-equal (file-remote-p "/[/1.2.3.4]" 'hop) nil))
1569
1570	  ;; Expand `tramp-default-method'.
1571	  (should (string-equal
1572		   (file-remote-p "/[/user@1.2.3.4]")
1573		   (format "/[%s/%s@%s]" "default-method" "user" "1.2.3.4")))
1574	  (should (string-equal
1575		   (file-remote-p
1576		    "/[/user@1.2.3.4]" 'method) "default-method"))
1577	  (should (string-equal
1578		   (file-remote-p "/[/user@1.2.3.4]" 'user) "user"))
1579	  (should (string-equal
1580		   (file-remote-p "/[/user@1.2.3.4]" 'host) "1.2.3.4"))
1581	  (should (string-equal
1582		   (file-remote-p "/[/user@1.2.3.4]" 'localname) ""))
1583	  (should (string-equal (file-remote-p "/[/user@1.2.3.4]" 'hop) nil))
1584
1585	  ;; Expand `tramp-default-method' and `tramp-default-user'.
1586	  (should (string-equal
1587		   (file-remote-p "/[-/1.2.3.4]")
1588		   (format
1589		    "/[%s/%s@%s]" "default-method" "default-user" "1.2.3.4")))
1590	  (should (string-equal
1591		   (file-remote-p "/[-/1.2.3.4]" 'method) "default-method"))
1592	  (should (string-equal
1593		   (file-remote-p "/[-/1.2.3.4]" 'user) "default-user"))
1594	  (should (string-equal
1595		   (file-remote-p "/[-/1.2.3.4]" 'host) "1.2.3.4"))
1596	  (should (string-equal (file-remote-p "/[-/1.2.3.4]" 'localname) ""))
1597	  (should (string-equal (file-remote-p "/[-/1.2.3.4]" 'hop) nil))
1598
1599	  ;; Expand `tramp-default-method'.
1600	  (should (string-equal
1601		   (file-remote-p "/[-/user@1.2.3.4]")
1602		   (format "/[%s/%s@%s]" "default-method" "user" "1.2.3.4")))
1603	  (should (string-equal
1604		   (file-remote-p
1605		    "/[-/user@1.2.3.4]" 'method) "default-method"))
1606	  (should (string-equal
1607		   (file-remote-p "/[-/user@1.2.3.4]" 'user) "user"))
1608	  (should (string-equal
1609		   (file-remote-p "/[-/user@1.2.3.4]" 'host) "1.2.3.4"))
1610	  (should (string-equal
1611		   (file-remote-p "/[-/user@1.2.3.4]" 'localname) ""))
1612	  (should (string-equal (file-remote-p "/[-/user@1.2.3.4]" 'hop) nil))
1613
1614	  ;; Expand `tramp-default-user'.
1615	  (should (string-equal
1616		   (file-remote-p "/[method/1.2.3.4]")
1617		   (format "/[%s/%s@%s]" "method" "default-user" "1.2.3.4")))
1618	  (should (string-equal
1619		   (file-remote-p "/[method/1.2.3.4]" 'method) "method"))
1620	  (should (string-equal
1621		   (file-remote-p "/[method/1.2.3.4]" 'user) "default-user"))
1622	  (should (string-equal
1623		   (file-remote-p "/[method/1.2.3.4]" 'host) "1.2.3.4"))
1624	  (should (string-equal
1625		   (file-remote-p "/[method/1.2.3.4]" 'localname) ""))
1626	  (should (string-equal (file-remote-p "/[method/1.2.3.4]" 'hop) nil))
1627
1628	  ;; No expansion.
1629	  (should (string-equal
1630		   (file-remote-p "/[method/user@1.2.3.4]")
1631		   (format "/[%s/%s@%s]" "method" "user" "1.2.3.4")))
1632	  (should (string-equal
1633		   (file-remote-p "/[method/user@1.2.3.4]" 'method) "method"))
1634	  (should (string-equal
1635		   (file-remote-p "/[method/user@1.2.3.4]" 'user) "user"))
1636	  (should (string-equal
1637		   (file-remote-p "/[method/user@1.2.3.4]" 'host) "1.2.3.4"))
1638	  (should (string-equal
1639		   (file-remote-p "/[method/user@1.2.3.4]" 'localname) ""))
1640	  (should (string-equal
1641		   (file-remote-p "/[method/user@1.2.3.4]" 'hop) nil))
1642
1643	  ;; Expand `tramp-default-method', `tramp-default-user' and
1644	  ;; `tramp-default-host'.
1645	  (should (string-equal
1646		   (file-remote-p "/[/]")
1647		   (format
1648		    "/[%s/%s@%s]"
1649		    "default-method" "default-user" "default-host")))
1650	  (should (string-equal
1651		   (file-remote-p "/[/]" 'method) "default-method"))
1652	  (should (string-equal (file-remote-p "/[/]" 'user) "default-user"))
1653	  (should (string-equal (file-remote-p "/[/]" 'host) "default-host"))
1654	  (should (string-equal (file-remote-p "/[/]" 'localname) ""))
1655	  (should (string-equal (file-remote-p "/[/]" 'hop) nil))
1656
1657	  ;; Expand `tramp-default-method' and `tramp-default-user'.
1658	  (let ((tramp-default-host "::1"))
1659	    (should (string-equal
1660		     (file-remote-p "/[/]")
1661		     (format
1662		      "/[%s/%s@%s]"
1663		      "default-method" "default-user" "::1")))
1664	    (should (string-equal
1665		     (file-remote-p "/[/]" 'method) "default-method"))
1666	    (should (string-equal (file-remote-p "/[/]" 'user) "default-user"))
1667	    (should (string-equal (file-remote-p "/[/]" 'host) "::1"))
1668	    (should (string-equal (file-remote-p "/[/]" 'localname) ""))
1669	    (should (string-equal (file-remote-p "/[/]" 'hop) nil)))
1670
1671	  ;; Expand `tramp-default-method' and `tramp-default-user'.
1672	  (should (string-equal
1673		   (file-remote-p "/[/::1]")
1674		   (format
1675		    "/[%s/%s@%s]" "default-method" "default-user" "::1")))
1676	  (should (string-equal
1677		   (file-remote-p "/[/::1]" 'method) "default-method"))
1678	  (should (string-equal
1679		   (file-remote-p "/[/::1]" 'user) "default-user"))
1680	  (should (string-equal (file-remote-p "/[/::1]" 'host) "::1"))
1681	  (should (string-equal (file-remote-p "/[/::1]" 'localname) ""))
1682	  (should (string-equal (file-remote-p "/[/::1]" 'hop) nil))
1683
1684	  ;; Expand `tramp-default-method'.
1685	  (should (string-equal
1686		   (file-remote-p "/[/user@::1]")
1687		   (format "/[%s/%s@%s]" "default-method" "user" "::1")))
1688	  (should (string-equal
1689		   (file-remote-p "/[/user@::1]" 'method) "default-method"))
1690	  (should (string-equal (file-remote-p "/[/user@::1]" 'user) "user"))
1691	  (should (string-equal (file-remote-p "/[/user@::1]" 'host) "::1"))
1692	  (should (string-equal (file-remote-p "/[/user@::1]" 'localname) ""))
1693	  (should (string-equal (file-remote-p "/[/user@::1]" 'hop) nil))
1694
1695	  ;; Expand `tramp-default-method', `tramp-default-user' and
1696	  ;; `tramp-default-host'.
1697	  (should (string-equal
1698		   (file-remote-p "/[-/]")
1699		   (format
1700		    "/[%s/%s@%s]"
1701		    "default-method" "default-user" "default-host")))
1702	  (should (string-equal
1703		   (file-remote-p "/[-/]" 'method) "default-method"))
1704	  (should (string-equal (file-remote-p "/[-/]" 'user) "default-user"))
1705	  (should (string-equal (file-remote-p "/[-/]" 'host) "default-host"))
1706	  (should (string-equal (file-remote-p "/[-/]" 'localname) ""))
1707	  (should (string-equal (file-remote-p "/[-/]" 'hop) nil))
1708
1709	  ;; Expand `tramp-default-method' and `tramp-default-user'.
1710	  (let ((tramp-default-host "::1"))
1711	    (should (string-equal
1712		     (file-remote-p "/[-/]")
1713		     (format
1714		      "/[%s/%s@%s]"
1715		      "default-method" "default-user" "::1")))
1716	    (should (string-equal
1717		     (file-remote-p "/[-/]" 'method) "default-method"))
1718	    (should (string-equal (file-remote-p "/[-/]" 'user) "default-user"))
1719	    (should (string-equal (file-remote-p "/[-/]" 'host) "::1"))
1720	    (should (string-equal (file-remote-p "/[-/]" 'localname) ""))
1721	    (should (string-equal (file-remote-p "/[-/]" 'hop) nil)))
1722
1723	  ;; Expand `tramp-default-method' and `tramp-default-user'.
1724	  (should (string-equal
1725		   (file-remote-p "/[-/::1]")
1726		   (format
1727		    "/[%s/%s@%s]" "default-method" "default-user" "::1")))
1728	  (should (string-equal
1729		   (file-remote-p "/[-/::1]" 'method) "default-method"))
1730	  (should (string-equal
1731		   (file-remote-p "/[-/::1]" 'user) "default-user"))
1732	  (should (string-equal (file-remote-p "/[-/::1]" 'host) "::1"))
1733	  (should (string-equal (file-remote-p "/[-/::1]" 'localname) ""))
1734	  (should (string-equal (file-remote-p "/[-/::1]" 'hop) nil))
1735
1736	  ;; Expand `tramp-default-method'.
1737	  (should (string-equal
1738		   (file-remote-p "/[-/user@::1]")
1739		   (format "/[%s/%s@%s]" "default-method" "user" "::1")))
1740	  (should (string-equal
1741		   (file-remote-p "/[-/user@::1]" 'method) "default-method"))
1742	  (should (string-equal (file-remote-p "/[-/user@::1]" 'user) "user"))
1743	  (should (string-equal (file-remote-p "/[-/user@::1]" 'host) "::1"))
1744	  (should (string-equal (file-remote-p "/[-/user@::1]" 'localname) ""))
1745	  (should (string-equal (file-remote-p "/[-/user@::1]" 'hop) nil))
1746
1747	  ;; Expand `tramp-default-user'.
1748	  (should (string-equal
1749		   (file-remote-p "/[method/::1]")
1750		   (format "/[%s/%s@%s]" "method" "default-user" "::1")))
1751	  (should (string-equal
1752		   (file-remote-p "/[method/::1]" 'method) "method"))
1753	  (should (string-equal
1754		   (file-remote-p "/[method/::1]" 'user) "default-user"))
1755	  (should (string-equal (file-remote-p "/[method/::1]" 'host) "::1"))
1756	  (should (string-equal (file-remote-p "/[method/::1]" 'localname) ""))
1757	  (should (string-equal (file-remote-p "/[method/::1]" 'hop) nil))
1758
1759	  ;; No expansion.
1760	  (should (string-equal
1761		   (file-remote-p "/[method/user@::1]")
1762		   (format "/[%s/%s@%s]" "method" "user" "::1")))
1763	  (should (string-equal
1764		   (file-remote-p "/[method/user@::1]" 'method) "method"))
1765	  (should (string-equal
1766		   (file-remote-p "/[method/user@::1]" 'user) "user"))
1767	  (should (string-equal
1768		   (file-remote-p "/[method/user@::1]" 'host) "::1"))
1769	  (should (string-equal
1770		   (file-remote-p "/[method/user@::1]" 'localname) ""))
1771	  (should (string-equal (file-remote-p "/[method/user@::1]" 'hop) nil))
1772
1773	  ;; Local file name part.
1774	  (should (string-equal (file-remote-p "/[/host]/:" 'localname) "/:"))
1775	  (should (string-equal (file-remote-p "/[-/host]/:" 'localname) "/:"))
1776	  (should (string-equal (file-remote-p "/[method/]:" 'localname) ":"))
1777	  (should (string-equal (file-remote-p "/[method/] " 'localname) " "))
1778	  (should (string-equal
1779		   (file-remote-p "/[method/]file" 'localname) "file"))
1780	  (should (string-equal
1781		   (file-remote-p "/[method/]/path/to/file" 'localname)
1782		   "/path/to/file"))
1783
1784	  ;; Multihop.
1785	  (should
1786	   (string-equal
1787	    (file-remote-p
1788	     "/[method1/user1@host1|method2/user2@host2]/path/to/file")
1789	    (format "/[%s/%s@%s|%s/%s@%s]"
1790		    "method1" "user1" "host1" "method2" "user2" "host2")))
1791	  (should
1792	   (string-equal
1793	    (file-remote-p
1794	     "/[method1/user1@host1|method2/user2@host2]/path/to/file" 'method)
1795	    "method2"))
1796	  (should
1797	   (string-equal
1798	    (file-remote-p
1799	     "/[method1/user1@host1|method2/user2@host2]/path/to/file" 'user)
1800	    "user2"))
1801	  (should
1802	   (string-equal
1803	    (file-remote-p
1804	     "/[method1/user1@host1|method2/user2@host2]/path/to/file" 'host)
1805	    "host2"))
1806	  (should
1807	   (string-equal
1808	    (file-remote-p
1809	     "/[method1/user1@host1|method2/user2@host2]/path/to/file"
1810	     'localname)
1811	    "/path/to/file"))
1812	  (should
1813	   (string-equal
1814	    (file-remote-p
1815	     "/[method1/user1@host1|method2/user2@host2]/path/to/file" 'hop)
1816	    (format "%s/%s@%s|"
1817		    "method1" "user1" "host1")))
1818
1819	  (should
1820	   (string-equal
1821	    (file-remote-p
1822	     (concat
1823	      "/[method1/user1@host1"
1824	      "|method2/user2@host2"
1825	      "|method3/user3@host3]/path/to/file"))
1826	    (format "/[%s/%s@%s|%s/%s@%s|%s/%s@%s]"
1827		    "method1" "user1" "host1"
1828		    "method2" "user2" "host2"
1829		    "method3" "user3" "host3")))
1830	  (should
1831	   (string-equal
1832	    (file-remote-p
1833	     (concat
1834	      "/[method1/user1@host1"
1835	      "|method2/user2@host2"
1836	      "|method3/user3@host3]/path/to/file")
1837	     'method)
1838	    "method3"))
1839	  (should
1840	   (string-equal
1841	    (file-remote-p
1842	     (concat
1843	      "/[method1/user1@host1"
1844	      "|method2/user2@host2"
1845	      "|method3/user3@host3]/path/to/file")
1846	     'user)
1847	    "user3"))
1848	  (should
1849	   (string-equal
1850	    (file-remote-p
1851	     (concat
1852	      "/[method1/user1@host1"
1853	      "|method2/user2@host2"
1854	      "|method3/user3@host3]/path/to/file")
1855	     'host)
1856	    "host3"))
1857	  (should
1858	   (string-equal
1859	    (file-remote-p
1860	     (concat
1861	      "/[method1/user1@host1"
1862	      "|method2/user2@host2"
1863	      "|method3/user3@host3]/path/to/file")
1864	     'localname)
1865	    "/path/to/file"))
1866	  (should
1867	   (string-equal
1868	    (file-remote-p
1869	     (concat
1870	      "/[method1/user1@host1"
1871	      "|method2/user2@host2"
1872	      "|method3/user3@host3]/path/to/file")
1873	     'hop)
1874	    (format "%s/%s@%s|%s/%s@%s|"
1875		    "method1" "user1" "host1" "method2" "user2" "host2")))
1876
1877	  ;; Expand `tramp-default-method-alist'.
1878	  (add-to-list 'tramp-default-method-alist '("host1" "user1" "method1"))
1879	  (add-to-list 'tramp-default-method-alist '("host2" "user2" "method2"))
1880	  (add-to-list 'tramp-default-method-alist '("host3" "user3" "method3"))
1881	  (should
1882	   (string-equal
1883	    (file-remote-p
1884	     (concat
1885	      "/[/user1@host1"
1886	      "|/user2@host2"
1887	      "|/user3@host3]/path/to/file"))
1888	    (format "/[%s/%s@%s|%s/%s@%s|%s/%s@%s]"
1889		    "method1" "user1" "host1"
1890		    "method2" "user2" "host2"
1891		    "method3" "user3" "host3")))
1892
1893	  ;; Expand `tramp-default-user-alist'.
1894	  (add-to-list 'tramp-default-user-alist '("method1" "host1" "user1"))
1895	  (add-to-list 'tramp-default-user-alist '("method2" "host2" "user2"))
1896	  (add-to-list 'tramp-default-user-alist '("method3" "host3" "user3"))
1897	  (should
1898	   (string-equal
1899	    (file-remote-p
1900	     (concat
1901	      "/[method1/host1"
1902	      "|method2/host2"
1903	      "|method3/host3]/path/to/file"))
1904	    (format "/[%s/%s@%s|%s/%s@%s|%s/%s@%s]"
1905		    "method1" "user1" "host1"
1906		    "method2" "user2" "host2"
1907		    "method3" "user3" "host3")))
1908
1909	  ;; Expand `tramp-default-host-alist'.
1910	  (add-to-list 'tramp-default-host-alist '("method1" "user1" "host1"))
1911	  (add-to-list 'tramp-default-host-alist '("method2" "user2" "host2"))
1912	  (add-to-list 'tramp-default-host-alist '("method3" "user3" "host3"))
1913	  (should
1914	   (string-equal
1915	    (file-remote-p
1916	     (concat
1917	      "/[method1/user1@"
1918	      "|method2/user2@"
1919	      "|method3/user3@]/path/to/file"))
1920	    (format "/[%s/%s@%s|%s/%s@%s|%s/%s@%s]"
1921		    "method1" "user1" "host1"
1922		    "method2" "user2" "host2"
1923		    "method3" "user3" "host3")))
1924
1925	  ;; Ad-hoc user name and host name expansion.
1926	  (setq tramp-default-method-alist nil
1927		tramp-default-user-alist nil
1928		tramp-default-host-alist nil)
1929	  (should
1930	   (string-equal
1931	    (file-remote-p
1932	     (concat
1933	      "/[method1/user1@host1"
1934	      "|method2/user2@"
1935	      "|method3/user3@]/path/to/file"))
1936	    (format "/[%s/%s@%s|%s/%s@%s|%s/%s@%s]"
1937		    "method1" "user1" "host1"
1938		    "method2" "user2" "host1"
1939		    "method3" "user3" "host1")))
1940	  (should
1941	   (string-equal
1942	    (file-remote-p
1943	     (concat
1944	      "/[method1/%u@%h"
1945	      "|method2/user2@host2"
1946	      "|method3/%u@%h"
1947	      "|method4/user4%domain4@host4#1234]/path/to/file"))
1948	    (format "/[%s/%s@%s|%s/%s@%s|%s/%s@%s|%s/%s@%s]"
1949		    "method1" "user2" "host2"
1950		    "method2" "user2" "host2"
1951		    "method3" "user4" "host4"
1952		    "method4" "user4%domain4" "host4#1234"))))
1953
1954      ;; Exit.
1955      (tramp-change-syntax syntax))))
1956
1957(ert-deftest tramp-test03-file-name-defaults ()
1958  "Check default values for some methods."
1959  (skip-unless (eq tramp-syntax 'default))
1960
1961  ;; Default values in tramp-adb.el.
1962  (when (assoc "adb" tramp-methods)
1963    (should (string-equal (file-remote-p "/adb::" 'host) "")))
1964  ;; Default values in tramp-ftp.el.
1965  (when (assoc "ftp" tramp-methods)
1966    (should (string-equal (file-remote-p "/-:ftp.host:" 'method) "ftp"))
1967    (dolist (u '("ftp" "anonymous"))
1968      (should
1969       (string-equal (file-remote-p (format "/-:%s@:" u) 'method) "ftp"))))
1970  ;; Default values in tramp-sh.el and tramp-sudoedit.el.
1971  (when (assoc "su" tramp-methods)
1972    (dolist (h `("127.0.0.1" "[::1]" "localhost" "localhost6" ,(system-name)))
1973      (should
1974       (string-equal (file-remote-p (format "/-:root@%s:" h) 'method) "su")))
1975    (dolist (m '("su" "sudo" "ksu" "doas" "sudoedit"))
1976      (should (string-equal (file-remote-p (format "/%s::" m) 'user) "root"))
1977      (should
1978       (string-equal (file-remote-p (format "/%s::" m) 'host) (system-name))))
1979    (dolist (m '("rcp" "remcp" "rsh" "telnet" "krlogin" "fcp" "nc"))
1980      (should
1981       (string-equal
1982	(file-remote-p (format "/%s::" m) 'user) (user-login-name)))))
1983  ;; Default values in tramp-smb.el.
1984  (when (assoc "smb" tramp-methods)
1985    (should (string-equal (file-remote-p "/smb::" 'user) nil))))
1986
1987;; The following test is inspired by Bug#30946.
1988(ert-deftest tramp-test03-file-name-host-rules ()
1989  "Check host name rules for host-less methods."
1990  (skip-unless (eq tramp-syntax 'default))
1991  (skip-unless (tramp--test-enabled))
1992  (skip-unless (tramp--test-sh-p))
1993
1994  ;; Host names must match rules in case the command template of a
1995  ;; method doesn't use them.
1996  (dolist (m '("su" "sg" "sudo" "doas" "ksu"))
1997    (let (tramp-connection-properties tramp-default-proxies-alist)
1998      (ignore-errors
1999	(tramp-cleanup-connection tramp-test-vec nil 'keep-password))
2000      ;; Single hop.  The host name must match `tramp-local-host-regexp'.
2001      (should-error
2002       (find-file (format "/%s:foo:" m))
2003       :type 'user-error)
2004      ;; Multi hop.  The host name must match the previous hop.
2005      (should-error
2006       (find-file
2007	(format
2008	 "%s|%s:foo:"
2009	 (substring (file-remote-p tramp-test-temporary-file-directory) 0 -1)
2010	 m))
2011       :type 'user-error))))
2012
2013(ert-deftest tramp-test03-file-name-method-rules ()
2014  "Check file name rules for some methods."
2015  (skip-unless (eq tramp-syntax 'default))
2016  (skip-unless (tramp--test-enabled))
2017
2018  ;; Multi hops are allowed for inline methods only.
2019  (let (non-essential)
2020    (should-error
2021     (expand-file-name "/ssh:user1@host1|method:user2@host2:/path/to/file")
2022     :type 'user-error)
2023    (should-error
2024     (expand-file-name "/method:user1@host1|ssh:user2@host2:/path/to/file")
2025     :type 'user-error))
2026
2027  ;; Samba does not support file names with periods followed by
2028  ;; spaces, and trailing periods or spaces.
2029  (when (tramp--test-smb-p)
2030    (dolist (file '("foo." "foo. bar" "foo "))
2031      (should-error
2032       (tramp-smb-get-localname
2033	(tramp-dissect-file-name
2034	 (expand-file-name file tramp-test-temporary-file-directory)))
2035       :type 'file-error))))
2036
2037(ert-deftest tramp-test04-substitute-in-file-name ()
2038  "Check `substitute-in-file-name'."
2039  (skip-unless (eq tramp-syntax 'default))
2040
2041  ;; Suppress method name check.  We cannot use the string "foo" as
2042  ;; user name, because (substitute-in-string "/~foo") returns
2043  ;; different values depending on the existence of user "foo" (see
2044  ;; Bug#43052).
2045  (let ((tramp-methods (cons '("method") tramp-methods))
2046        (foo (downcase (md5 (current-time-string)))))
2047    (should
2048     (string-equal (substitute-in-file-name "/method:host:///foo") "/foo"))
2049    (should
2050     (string-equal
2051      (substitute-in-file-name "/method:host://foo") "/method:host:/foo"))
2052    (should
2053     (string-equal (substitute-in-file-name "/method:host:/path///foo") "/foo"))
2054    (should
2055     (string-equal
2056      (substitute-in-file-name "/method:host:/path//foo") "/method:host:/foo"))
2057    ;; Quoting local part.
2058    (should
2059     (string-equal
2060      (substitute-in-file-name "/method:host:/:///foo")
2061      "/method:host:/:///foo"))
2062    (should
2063     (string-equal
2064      (substitute-in-file-name "/method:host:/://foo") "/method:host:/://foo"))
2065    (should
2066     (string-equal
2067      (substitute-in-file-name "/method:host:/:/path///foo")
2068      "/method:host:/:/path///foo"))
2069    (should
2070     (string-equal
2071      (substitute-in-file-name "/method:host:/:/path//foo")
2072      "/method:host:/:/path//foo"))
2073
2074    (should
2075     (string-equal
2076      (substitute-in-file-name (concat "/method:host://~" foo))
2077      (concat "/~" foo)))
2078    (should
2079     (string-equal
2080      (substitute-in-file-name (concat "/method:host:/~" foo))
2081      (concat "/method:host:/~" foo)))
2082    (should
2083     (string-equal
2084      (substitute-in-file-name (concat "/method:host:/path//~" foo))
2085      (concat "/~" foo)))
2086    ;; (substitute-in-file-name "/path/~foo") expands only for a local
2087    ;; user "foo" to "/~foo"".  Otherwise, it doesn't expand.
2088    (should
2089     (string-equal
2090      (substitute-in-file-name (concat "/method:host:/path/~" foo))
2091      (concat "/method:host:/path/~" foo)))
2092    ;; Quoting local part.
2093    (should
2094     (string-equal
2095      (substitute-in-file-name (concat "/method:host:/://~" foo))
2096      (concat "/method:host:/://~" foo)))
2097    (should
2098     (string-equal
2099      (substitute-in-file-name (concat "/method:host:/:/~" foo))
2100      (concat "/method:host:/:/~" foo)))
2101    (should
2102     (string-equal
2103      (substitute-in-file-name (concat "/method:host:/:/path//~" foo))
2104      (concat "/method:host:/:/path//~" foo)))
2105    (should
2106     (string-equal
2107      (substitute-in-file-name (concat "/method:host:/:/path/~" foo))
2108      (concat "/method:host:/:/path/~" foo)))
2109
2110    (let (process-environment)
2111      (should
2112       (string-equal
2113	(substitute-in-file-name "/method:host:/path/$FOO")
2114	"/method:host:/path/$FOO"))
2115      (setenv "FOO" "bla")
2116      (should
2117       (string-equal
2118	(substitute-in-file-name "/method:host:/path/$FOO")
2119	"/method:host:/path/bla"))
2120      (should
2121       (string-equal
2122	(substitute-in-file-name "/method:host:/path/$$FOO")
2123	"/method:host:/path/$FOO"))
2124      ;; Quoting local part.
2125      (should
2126       (string-equal
2127	(substitute-in-file-name "/method:host:/:/path/$FOO")
2128	"/method:host:/:/path/$FOO"))
2129      (setenv "FOO" "bla")
2130      (should
2131       (string-equal
2132	(substitute-in-file-name "/method:host:/:/path/$FOO")
2133	"/method:host:/:/path/$FOO"))
2134      (should
2135       (string-equal
2136	(substitute-in-file-name "/method:host:/:/path/$$FOO")
2137	"/method:host:/:/path/$$FOO")))))
2138
2139(ert-deftest tramp-test05-expand-file-name ()
2140  "Check `expand-file-name'."
2141  (skip-unless (eq tramp-syntax 'default))
2142
2143  ;; Suppress method name check.
2144  (let ((tramp-methods (cons '("method") tramp-methods)))
2145    (should
2146     (string-equal
2147      (expand-file-name "/method:host:/path/./file") "/method:host:/path/file"))
2148    (should
2149     (string-equal
2150      (expand-file-name "/method:host:/path/../file") "/method:host:/file"))
2151    (should
2152     (string-equal
2153      (expand-file-name "/method:host:/path/.") "/method:host:/path"))
2154    (should
2155     (string-equal
2156      (expand-file-name "/method:host:/path/..") "/method:host:/"))
2157    (should
2158     (string-equal
2159      (expand-file-name "." "/method:host:/path/") "/method:host:/path"))
2160    (should
2161     (string-equal
2162      (expand-file-name "" "/method:host:/path/") "/method:host:/path"))
2163    ;; Quoting local part.
2164    (should
2165     (string-equal
2166      (expand-file-name "/method:host:/:/path/./file")
2167      "/method:host:/:/path/file"))
2168    (should
2169     (string-equal
2170      (expand-file-name "/method:host:/:/path/../file") "/method:host:/:/file"))
2171    (should
2172     (string-equal
2173      (expand-file-name "/method:host:/:/~/path/./file")
2174      "/method:host:/:/~/path/file"))))
2175
2176;; The following test is inspired by Bug#26911 and Bug#34834.  They
2177;; were bugs in `expand-file-name'.
2178(ert-deftest tramp-test05-expand-file-name-relative ()
2179  "Check `expand-file-name'."
2180  (skip-unless (tramp--test-enabled))
2181  ;; The bugs are fixed in Emacs 28.1.
2182  (skip-unless (tramp--test-emacs28-p))
2183  ;; Methods with a share do not expand "/path/..".
2184  (skip-unless (not (tramp--test-share-p)))
2185
2186  (should
2187   (string-equal
2188    (let ((default-directory
2189	    (concat
2190	     (file-remote-p tramp-test-temporary-file-directory) "/path")))
2191      (expand-file-name ".." "./"))
2192    (concat (file-remote-p tramp-test-temporary-file-directory) "/"))))
2193
2194(ert-deftest tramp-test05-expand-file-name-top ()
2195  "Check `expand-file-name'."
2196  (skip-unless (tramp--test-enabled))
2197  (skip-unless (not (tramp--test-ange-ftp-p)))
2198
2199  (let ((dir (concat (file-remote-p tramp-test-temporary-file-directory) "/")))
2200    (dolist (local '("." ".."))
2201      (should (string-equal (expand-file-name local dir) dir))
2202      (should (string-equal (expand-file-name (concat dir local)) dir)))))
2203
2204(ert-deftest tramp-test06-directory-file-name ()
2205  "Check `directory-file-name'.
2206This checks also `file-name-as-directory', `file-name-directory',
2207`file-name-nondirectory' and `unhandled-file-name-directory'."
2208  (skip-unless (eq tramp-syntax 'default))
2209
2210  ;; Suppress method name check.
2211  (let ((tramp-methods (cons '("method") tramp-methods)))
2212    (should
2213     (string-equal
2214      (directory-file-name "/method:host:/path/to/file")
2215      "/method:host:/path/to/file"))
2216    (should
2217     (string-equal
2218      (directory-file-name "/method:host:/path/to/file/")
2219      "/method:host:/path/to/file"))
2220    (should
2221     (string-equal
2222      (directory-file-name "/method:host:/path/to/file//")
2223      "/method:host:/path/to/file"))
2224    (should
2225     (string-equal
2226      (file-name-as-directory "/method:host:/path/to/file")
2227      "/method:host:/path/to/file/"))
2228    (should
2229     (string-equal
2230      (file-name-as-directory "/method:host:/path/to/file/")
2231      "/method:host:/path/to/file/"))
2232    (should
2233     (string-equal
2234      (file-name-directory "/method:host:/path/to/file")
2235      "/method:host:/path/to/"))
2236    (should
2237     (string-equal
2238      (file-name-directory "/method:host:/path/to/file/")
2239      "/method:host:/path/to/file/"))
2240    (should
2241     (string-equal (file-name-directory "/method:host:file") "/method:host:"))
2242    (should
2243     (string-equal
2244      (file-name-directory "/method:host:path/") "/method:host:path/"))
2245    (should
2246     (string-equal
2247      (file-name-directory "/method:host:path/to") "/method:host:path/"))
2248    (should
2249     (string-equal
2250      (file-name-nondirectory "/method:host:/path/to/file") "file"))
2251    (should
2252     (string-equal (file-name-nondirectory "/method:host:/path/to/file/") ""))
2253    (should-not
2254     (unhandled-file-name-directory "/method:host:/path/to/file")))
2255
2256  ;; Bug#10085.
2257  (when (tramp--test-enabled) ;; Packages like tramp-gvfs.el might be disabled.
2258    (dolist (non-essential '(nil t))
2259      ;; We must clear `tramp-default-method'.  On hydra, it is "ftp",
2260      ;; which ruins the tests.
2261      (let ((tramp-default-method
2262	     (file-remote-p tramp-test-temporary-file-directory 'method))
2263	    (host (file-remote-p tramp-test-temporary-file-directory 'host)))
2264	(dolist
2265	    (file
2266	     `(,(format "/%s::" tramp-default-method)
2267	       ,(format
2268		 "/-:%s:"
2269		 (if (string-match-p tramp-ipv6-regexp host)
2270		     (concat
2271		      tramp-prefix-ipv6-format host tramp-postfix-ipv6-format)
2272		   host))))
2273	  (should (string-equal (directory-file-name file) file))
2274	  (should
2275	   (string-equal
2276	    (file-name-as-directory file)
2277	    (if non-essential
2278		file (concat file (if (tramp--test-ange-ftp-p) "/" "./")))))
2279	  (should (string-equal (file-name-directory file) file))
2280	  (should (string-equal (file-name-nondirectory file) "")))))))
2281
2282(ert-deftest tramp-test07-abbreviate-file-name ()
2283  "Check that Tramp abbreviates file names correctly."
2284  (skip-unless (tramp--test-enabled))
2285  (skip-unless (tramp--test-emacs29-p))
2286  (skip-unless (not (tramp--test-ange-ftp-p)))
2287
2288  (let* ((remote-host (file-remote-p tramp-test-temporary-file-directory))
2289	 ;; Not all methods can expand "~".
2290         (home-dir (ignore-errors (expand-file-name (concat remote-host "~")))))
2291    (skip-unless home-dir)
2292
2293    ;; Check home-dir abbreviation.
2294    (unless (string-suffix-p "~" home-dir)
2295      (should (equal (abbreviate-file-name (concat home-dir "/foo/bar"))
2296                     (concat remote-host "~/foo/bar")))
2297      (should (equal (abbreviate-file-name
2298		      (concat remote-host "/nowhere/special"))
2299                     (concat remote-host "/nowhere/special"))))
2300
2301    ;; Check `directory-abbrev-alist' abbreviation.
2302    (let ((directory-abbrev-alist
2303           `((,(concat "\\`" (regexp-quote home-dir) "/foo")
2304              . ,(concat home-dir "/f"))
2305             (,(concat "\\`" (regexp-quote remote-host) "/nowhere")
2306              . ,(concat remote-host "/nw")))))
2307      (should (equal (abbreviate-file-name (concat home-dir "/foo/bar"))
2308                     (concat remote-host "~/f/bar")))
2309      (should (equal (abbreviate-file-name
2310		      (concat remote-host "/nowhere/special"))
2311                     (concat remote-host "/nw/special"))))
2312
2313    ;; Check that home-dir abbreviation doesn't occur when home-dir is just "/".
2314    (setq home-dir (concat remote-host "/"))
2315    ;; The remote home directory is kept in the connection property
2316    ;; "home-directory".  We fake this setting.
2317    (tramp-set-connection-property tramp-test-vec "home-directory" home-dir)
2318    (should (equal (concat home-dir "foo/bar")
2319                   (abbreviate-file-name (concat home-dir "foo/bar"))))
2320    (tramp-flush-connection-property tramp-test-vec "home-directory")))
2321
2322(ert-deftest tramp-test07-file-exists-p ()
2323  "Check `file-exist-p', `write-region' and `delete-file'."
2324  (skip-unless (tramp--test-enabled))
2325
2326  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
2327    (let ((tmp-name (tramp--test-make-temp-name nil quoted)))
2328      (should-not (file-exists-p tmp-name))
2329      (write-region "foo" nil tmp-name)
2330      (should (file-exists-p tmp-name))
2331      (delete-file tmp-name)
2332      (should-not (file-exists-p tmp-name))
2333
2334      ;; Trashing files doesn't work when `system-move-file-to-trash'
2335      ;; is defined (on MS Windows and macOS), and for crypted remote
2336      ;; files.
2337      (unless (or (fboundp 'system-move-file-to-trash) (tramp--test-crypt-p))
2338	(let ((trash-directory (tramp--test-make-temp-name 'local quoted))
2339	      (delete-by-moving-to-trash t))
2340	  (make-directory trash-directory)
2341	  (should-not (file-exists-p tmp-name))
2342	  (write-region "foo" nil tmp-name)
2343	  (should (file-exists-p tmp-name))
2344	  (delete-file tmp-name 'trash)
2345	  (should-not (file-exists-p tmp-name))
2346	  (should
2347	   (or (file-exists-p
2348		(expand-file-name
2349		 (file-name-nondirectory tmp-name) trash-directory))
2350	       ;; Gdrive.
2351	       (file-symlink-p
2352		(expand-file-name
2353		 (file-name-nondirectory tmp-name) trash-directory))))
2354	  (delete-directory trash-directory 'recursive)
2355	  (should-not (file-exists-p trash-directory)))))))
2356
2357(ert-deftest tramp-test08-file-local-copy ()
2358  "Check `file-local-copy'."
2359  (skip-unless (tramp--test-enabled))
2360
2361  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
2362    (let ((tmp-name1 (tramp--test-make-temp-name nil quoted))
2363	  tmp-name2)
2364      (unwind-protect
2365	  (progn
2366	    (write-region "foo" nil tmp-name1)
2367	    (should (setq tmp-name2 (file-local-copy tmp-name1)))
2368	    (with-temp-buffer
2369	      (insert-file-contents tmp-name2)
2370	      (should (string-equal (buffer-string) "foo")))
2371	    ;; Check also that a file transfer with compression works.
2372	    (let ((default-directory tramp-test-temporary-file-directory)
2373		  (tramp-copy-size-limit 4)
2374		  (tramp-inline-compress-start-size 2))
2375	      (delete-file tmp-name2)
2376	      (should (setq tmp-name2 (file-local-copy tmp-name1))))
2377	    ;; Error case.
2378	    (delete-file tmp-name1)
2379	    (delete-file tmp-name2)
2380	    (should-error
2381	     (setq tmp-name2 (file-local-copy tmp-name1))
2382	     :type 'file-missing))
2383
2384	;; Cleanup.
2385	(ignore-errors
2386	  (delete-file tmp-name1)
2387	  (delete-file tmp-name2))))))
2388
2389(ert-deftest tramp-test09-insert-file-contents ()
2390  "Check `insert-file-contents'."
2391  (skip-unless (tramp--test-enabled))
2392
2393  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
2394    (let ((tmp-name (tramp--test-make-temp-name nil quoted)))
2395      (unwind-protect
2396	  (with-temp-buffer
2397	    (write-region "foo" nil tmp-name)
2398	    (let ((point (point)))
2399	      (insert-file-contents tmp-name)
2400	      (should (string-equal (buffer-string) "foo"))
2401	      (should (= point (point))))
2402	    (goto-char (1+ (point)))
2403	    (let ((point (point)))
2404	      (insert-file-contents tmp-name)
2405	      (should (string-equal (buffer-string) "ffoooo"))
2406	      (should (= point (point))))
2407	    ;; Insert partly.
2408	    (let ((point (point)))
2409	      (insert-file-contents tmp-name nil 1 3)
2410	      (should (string-equal (buffer-string) "foofoooo"))
2411	      (should (= point (point))))
2412	    ;; Replace.
2413	    (let ((point (point)))
2414	      (insert-file-contents tmp-name nil nil nil 'replace)
2415	      (should (string-equal (buffer-string) "foo"))
2416	      (should (= point (point))))
2417	    ;; Error case.
2418	    (delete-file tmp-name)
2419	    (should-error
2420	     (insert-file-contents tmp-name)
2421	     :type 'file-missing))
2422
2423	;; Cleanup.
2424	(ignore-errors (delete-file tmp-name))))))
2425
2426(ert-deftest tramp-test10-write-region ()
2427  "Check `write-region'."
2428  (skip-unless (tramp--test-enabled))
2429
2430  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
2431    (let ((tmp-name (tramp--test-make-temp-name nil quoted))
2432          (inhibit-message t))
2433      (unwind-protect
2434	  (progn
2435            ;; Write buffer.  Use absolute and relative file name.
2436	    (with-temp-buffer
2437	      (insert "foo")
2438	      (write-region nil nil tmp-name))
2439	    (with-temp-buffer
2440	      (insert-file-contents tmp-name)
2441	      (should (string-equal (buffer-string) "foo")))
2442	    (delete-file tmp-name)
2443	    (with-temp-buffer
2444	      (insert "foo")
2445	      (should-not (file-exists-p tmp-name))
2446	      (let ((default-directory (file-name-directory tmp-name)))
2447		(should-not (file-exists-p (file-name-nondirectory tmp-name)))
2448		(write-region nil nil (file-name-nondirectory tmp-name))
2449		(should (file-exists-p (file-name-nondirectory tmp-name))))
2450	      (should (file-exists-p tmp-name)))
2451	    (with-temp-buffer
2452	      (insert-file-contents tmp-name)
2453	      (should (string-equal (buffer-string) "foo")))
2454
2455	    ;; Append.
2456	    (unless (tramp--test-ange-ftp-p)
2457	      (with-temp-buffer
2458		(insert "bla")
2459		(write-region nil nil tmp-name 'append))
2460	      (with-temp-buffer
2461		(insert-file-contents tmp-name)
2462		(should (string-equal (buffer-string) "foobla")))
2463	      (with-temp-buffer
2464		(insert "baz")
2465		(write-region nil nil tmp-name 3))
2466	      (with-temp-buffer
2467		(insert-file-contents tmp-name)
2468		(should (string-equal (buffer-string) "foobaz")))
2469	      (delete-file tmp-name)
2470	      (with-temp-buffer
2471		(insert "foo")
2472		(write-region nil nil tmp-name 'append))
2473	      (with-temp-buffer
2474		(insert-file-contents tmp-name)
2475		(should (string-equal (buffer-string) "foo"))))
2476
2477	    ;; Write string.
2478	    (write-region "foo" nil tmp-name)
2479	    (with-temp-buffer
2480	      (insert-file-contents tmp-name)
2481	      (should (string-equal (buffer-string) "foo")))
2482
2483	    ;; Write partly.
2484	    (with-temp-buffer
2485	      (insert "123456789")
2486	      (write-region 3 5 tmp-name))
2487	    (with-temp-buffer
2488	      (insert-file-contents tmp-name)
2489	      (should (string-equal (buffer-string) "34")))
2490
2491	    ;; Check message.
2492	    (let (inhibit-message)
2493	      (dolist (noninteractive (unless (tramp--test-ange-ftp-p) '(nil t)))
2494		(dolist (visit '(nil t "string" no-message))
2495		  (ert-with-message-capture tramp--test-messages
2496		    (write-region "foo" nil tmp-name nil visit)
2497		    ;; We must check the last line.  There could be
2498		    ;; other messages from the progress reporter.
2499		    (should
2500		     (string-match-p
2501		      (if (and (null noninteractive)
2502			       (or (eq visit t) (null visit) (stringp visit)))
2503			  (format "^Wrote %s\n\\'" (regexp-quote tmp-name))
2504			"^\\'")
2505		      tramp--test-messages))))))
2506
2507	    ;; We do not test lockname here.  See
2508	    ;; `tramp-test39-make-lock-file-name'.
2509
2510	    ;; Do not overwrite if excluded.
2511	    (cl-letf (((symbol-function #'y-or-n-p) #'tramp--test-always)
2512		      ;; Ange-FTP.
2513		      ((symbol-function 'yes-or-no-p) #'tramp--test-always))
2514	      (write-region "foo" nil tmp-name nil nil nil 'mustbenew))
2515	    (should-error
2516	     (cl-letf (((symbol-function #'y-or-n-p) #'ignore)
2517		       ;; Ange-FTP.
2518		       ((symbol-function #'yes-or-no-p) #'ignore))
2519	       (write-region "foo" nil tmp-name nil nil nil 'mustbenew))
2520             :type 'file-already-exists)
2521	    (should-error
2522	     (write-region "foo" nil tmp-name nil nil nil 'excl)
2523	     :type 'file-already-exists))
2524
2525	;; Cleanup.
2526	(ignore-errors (delete-file tmp-name))))))
2527
2528;; The following test is inspired by Bug#35497.
2529(ert-deftest tramp-test10-write-region-file-precious-flag ()
2530  "Check that `file-precious-flag' is respected with Tramp in use."
2531  (skip-unless (tramp--test-enabled))
2532  (skip-unless (tramp--test-sh-p))
2533  ;; The bug is fixed in Emacs 27.1.
2534  (skip-unless (tramp--test-emacs27-p))
2535
2536  (let* ((tmp-name (tramp--test-make-temp-name))
2537         (inhibit-message t)
2538         written-files
2539         (advice (lambda (_start _end filename &rest _r)
2540                   (push filename written-files))))
2541
2542    (unwind-protect
2543        (with-current-buffer (find-file-noselect tmp-name)
2544          ;; Write initial contents.  Adapt `visited-file-modtime'
2545          ;; in order to suppress confirmation.
2546          (insert "foo")
2547          (write-region nil nil tmp-name)
2548          (set-visited-file-modtime)
2549          ;; Run the test.
2550          (advice-add 'write-region :before advice)
2551          (setq-local file-precious-flag t)
2552          (setq-local backup-inhibited t)
2553          (insert "bar")
2554          (should (null (save-buffer)))
2555          (should-not (cl-member tmp-name written-files :test #'string=)))
2556
2557      ;; Cleanup.
2558      (ignore-errors (advice-remove 'write-region advice))
2559      (ignore-errors (delete-file tmp-name)))))
2560
2561(ert-deftest tramp-test11-copy-file ()
2562  "Check `copy-file'."
2563  (skip-unless (tramp--test-enabled))
2564
2565  ;; `filename-non-special' has been fixed in Emacs 27.1, see Bug#29579.
2566  (dolist (quoted
2567	   (if (and (tramp--test-expensive-test-p) (tramp--test-emacs27-p))
2568	       '(nil t) '(nil)))
2569    (let ((tmp-name1 (tramp--test-make-temp-name nil quoted))
2570	  (tmp-name2 (tramp--test-make-temp-name nil quoted))
2571	  (tmp-name3 (tramp--test-make-temp-name 'local quoted)))
2572      (dolist (source-target
2573	       `(;; Copy on remote side.
2574		 (,tmp-name1 . ,tmp-name2)
2575		 ;; Copy from remote side to local side.
2576		 (,tmp-name1 . ,tmp-name3)
2577		 ;; Copy from local side to remote side.
2578		 (,tmp-name3 . ,tmp-name1)))
2579	(let ((source (car source-target))
2580	      (target (cdr source-target)))
2581
2582	  ;; Copy simple file.
2583	  (unwind-protect
2584	      (progn
2585		(should-error
2586		 (copy-file source target)
2587		 :type 'file-missing)
2588		(write-region "foo" nil source)
2589		(should (file-exists-p source))
2590		(copy-file source target)
2591		(should (file-exists-p target))
2592		(with-temp-buffer
2593		  (insert-file-contents target)
2594		  (should (string-equal (buffer-string) "foo")))
2595		(when (tramp--test-expensive-test-p)
2596		  (should-error
2597		   (copy-file source target)
2598		   :type 'file-already-exists))
2599		(copy-file source target 'ok))
2600
2601	    ;; Cleanup.
2602	    (ignore-errors (delete-file source))
2603	    (ignore-errors (delete-file target)))
2604
2605	  ;; Copy file to directory.
2606	  (unwind-protect
2607	      ;; This doesn't work on FTP.
2608	      (unless (tramp--test-ange-ftp-p)
2609		(write-region "foo" nil source)
2610		(should (file-exists-p source))
2611		(make-directory target)
2612		(should (file-directory-p target))
2613		(when (tramp--test-expensive-test-p)
2614		  (should-error
2615		   (copy-file source target)
2616		   :type 'file-already-exists)
2617		  (should-error
2618		   (copy-file source target 'ok)
2619		   :type 'file-error))
2620		(copy-file source (file-name-as-directory target))
2621		(should
2622		 (file-exists-p
2623		  (expand-file-name (file-name-nondirectory source) target))))
2624
2625	    ;; Cleanup.
2626	    (ignore-errors (delete-file source))
2627	    (ignore-errors (delete-directory target 'recursive)))
2628
2629	  ;; Copy directory to existing directory.
2630	  (unwind-protect
2631	      ;; This doesn't work on FTP.
2632	      (unless (tramp--test-ange-ftp-p)
2633		(make-directory source)
2634		(should (file-directory-p source))
2635		(write-region "foo" nil (expand-file-name "foo" source))
2636		(should (file-exists-p (expand-file-name "foo" source)))
2637		(make-directory target)
2638		(should (file-directory-p target))
2639		;; Directory `target' exists already, so we must use
2640		;; `file-name-as-directory'.
2641		(copy-file source (file-name-as-directory target))
2642		(should
2643		 (file-exists-p
2644		  (expand-file-name
2645		   (concat (file-name-nondirectory source) "/foo") target))))
2646
2647	    ;; Cleanup.
2648	    (ignore-errors (delete-directory source 'recursive))
2649	    (ignore-errors (delete-directory target 'recursive)))
2650
2651	  ;; Copy directory/file to non-existing directory.
2652	  (unwind-protect
2653	      ;; This doesn't work on FTP.
2654	      (unless (tramp--test-ange-ftp-p)
2655		(make-directory source)
2656		(should (file-directory-p source))
2657		(write-region "foo" nil (expand-file-name "foo" source))
2658		(should (file-exists-p (expand-file-name "foo" source)))
2659		(make-directory target)
2660		(should (file-directory-p target))
2661		(copy-file
2662		 source
2663		 (expand-file-name (file-name-nondirectory source) target))
2664		(should
2665		 (file-exists-p
2666		  (expand-file-name
2667		   (concat (file-name-nondirectory source) "/foo") target))))
2668
2669	    ;; Cleanup.
2670	    (ignore-errors (delete-directory source 'recursive))
2671	    (ignore-errors (delete-directory target 'recursive))))))))
2672
2673(ert-deftest tramp-test12-rename-file ()
2674  "Check `rename-file'."
2675  (skip-unless (tramp--test-enabled))
2676
2677  ;; `filename-non-special' has been fixed in Emacs 27.1, see Bug#29579.
2678  (dolist (quoted
2679	   (if (and (tramp--test-expensive-test-p) (tramp--test-emacs27-p))
2680	       '(nil t) '(nil)))
2681    (let ((tmp-name1 (tramp--test-make-temp-name nil quoted))
2682	  (tmp-name2 (tramp--test-make-temp-name nil quoted))
2683	  (tmp-name3 (tramp--test-make-temp-name 'local quoted)))
2684      (dolist (source-target
2685	       `(;; Rename on remote side.
2686		 (,tmp-name1 . ,tmp-name2)
2687		 ;; Rename from remote side to local side.
2688		 (,tmp-name1 . ,tmp-name3)
2689		 ;; Rename from local side to remote side.
2690		 (,tmp-name3 . ,tmp-name1)))
2691	(let ((source (car source-target))
2692	      (target (cdr source-target)))
2693
2694	  ;; Rename simple file.
2695	  (unwind-protect
2696	      (progn
2697		(should-error
2698		 (rename-file source target)
2699		 :type 'file-missing)
2700		(write-region "foo" nil source)
2701		(should (file-exists-p source))
2702		(rename-file source target)
2703		(should-not (file-exists-p source))
2704		(should (file-exists-p target))
2705		(with-temp-buffer
2706		  (insert-file-contents target)
2707		  (should (string-equal (buffer-string) "foo")))
2708		(write-region "foo" nil source)
2709		(should (file-exists-p source))
2710		(when (tramp--test-expensive-test-p)
2711		  (should-error
2712		   (rename-file source target)
2713		   :type 'file-already-exists))
2714		(rename-file source target 'ok)
2715		(should-not (file-exists-p source)))
2716
2717	    ;; Cleanup.
2718	    (ignore-errors (delete-file source))
2719	    (ignore-errors (delete-file target)))
2720
2721	  ;; Rename file to directory.
2722	  (unwind-protect
2723	      (progn
2724		(write-region "foo" nil source)
2725		(should (file-exists-p source))
2726		(make-directory target)
2727		(should (file-directory-p target))
2728		(when (tramp--test-expensive-test-p)
2729		  (should-error
2730		   (rename-file source target)
2731		   :type 'file-already-exists)
2732		  (should-error
2733		   (rename-file source target 'ok)
2734		   :type 'file-error))
2735		(rename-file source (file-name-as-directory target))
2736		(should-not (file-exists-p source))
2737		(should
2738		 (file-exists-p
2739		  (expand-file-name (file-name-nondirectory source) target))))
2740
2741	    ;; Cleanup.
2742	    (ignore-errors (delete-file source))
2743	    (ignore-errors (delete-directory target 'recursive)))
2744
2745	  ;; Rename directory to existing directory.
2746	  (unwind-protect
2747	      ;; This doesn't work on FTP.
2748	      (unless (tramp--test-ange-ftp-p)
2749		(make-directory source)
2750		(should (file-directory-p source))
2751		(write-region "foo" nil (expand-file-name "foo" source))
2752		(should (file-exists-p (expand-file-name "foo" source)))
2753		(make-directory target)
2754		(should (file-directory-p target))
2755		;; Directory `target' exists already, so we must use
2756		;; `file-name-as-directory'.
2757		(rename-file source (file-name-as-directory target))
2758		(should-not (file-exists-p source))
2759		(should
2760		 (file-exists-p
2761		  (expand-file-name
2762		   (concat (file-name-nondirectory source) "/foo") target))))
2763
2764	    ;; Cleanup.
2765	    (ignore-errors (delete-directory source 'recursive))
2766	    (ignore-errors (delete-directory target 'recursive)))
2767
2768	  ;; Rename directory/file to non-existing directory.
2769	  (unwind-protect
2770	      ;; This doesn't work on FTP.
2771	      (unless (tramp--test-ange-ftp-p)
2772		(make-directory source)
2773		(should (file-directory-p source))
2774		(write-region "foo" nil (expand-file-name "foo" source))
2775		(should (file-exists-p (expand-file-name "foo" source)))
2776		(make-directory target)
2777		(should (file-directory-p target))
2778		(rename-file
2779		 source
2780		 (expand-file-name (file-name-nondirectory source) target))
2781		(should-not (file-exists-p source))
2782		(should
2783		 (file-exists-p
2784		  (expand-file-name
2785		   (concat (file-name-nondirectory source) "/foo") target))))
2786
2787	    ;; Cleanup.
2788	    (ignore-errors (delete-directory source 'recursive))
2789	    (ignore-errors (delete-directory target 'recursive))))))))
2790
2791(ert-deftest tramp-test13-make-directory ()
2792  "Check `make-directory'.
2793This tests also `file-directory-p' and `file-accessible-directory-p'."
2794  (skip-unless (tramp--test-enabled))
2795
2796  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
2797    (let* ((tmp-name1 (tramp--test-make-temp-name nil quoted))
2798	   (tmp-name2 (expand-file-name "foo/bar" tmp-name1))
2799	   (unusual-file-mode-1 #o740)
2800	   (unusual-file-mode-2 #o710))
2801      (unwind-protect
2802	  (progn
2803	    (with-file-modes unusual-file-mode-1
2804	      (make-directory tmp-name1))
2805	    (should-error
2806	     (make-directory tmp-name1)
2807	     :type 'file-already-exists)
2808	    (should (file-directory-p tmp-name1))
2809	    (should (file-accessible-directory-p tmp-name1))
2810	    (when (tramp--test-supports-set-file-modes-p)
2811	      (should (equal (format "%#o" unusual-file-mode-1)
2812			     (format "%#o" (file-modes tmp-name1)))))
2813	    (should-error
2814	     (make-directory tmp-name2)
2815	     :type 'file-error)
2816	    (with-file-modes unusual-file-mode-2
2817	      (make-directory tmp-name2 'parents))
2818	    (should (file-directory-p tmp-name2))
2819	    (should (file-accessible-directory-p tmp-name2))
2820	    (when (tramp--test-supports-set-file-modes-p)
2821	      (should (equal (format "%#o" unusual-file-mode-2)
2822			     (format "%#o" (file-modes tmp-name2)))))
2823	    ;; If PARENTS is non-nil, `make-directory' shall not
2824	    ;; signal an error when DIR exists already.
2825	    (make-directory tmp-name2 'parents))
2826
2827	;; Cleanup.
2828	(ignore-errors (delete-directory tmp-name1 'recursive))))))
2829
2830(ert-deftest tramp-test14-delete-directory ()
2831  "Check `delete-directory'."
2832  (skip-unless (tramp--test-enabled))
2833
2834  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
2835    (let* ((tmp-name1 (tramp--test-make-temp-name nil quoted))
2836	   (tmp-name2 (expand-file-name "foo" tmp-name1)))
2837      ;; Delete empty directory.
2838      (make-directory tmp-name1)
2839      (should (file-directory-p tmp-name1))
2840      (delete-directory tmp-name1)
2841      (should-not (file-directory-p tmp-name1))
2842      ;; Delete non-empty directory.
2843      (make-directory tmp-name1)
2844      (should (file-directory-p tmp-name1))
2845      (write-region "foo" nil (expand-file-name "bla" tmp-name1))
2846      (should (file-exists-p (expand-file-name "bla" tmp-name1)))
2847      (make-directory tmp-name2)
2848      (should (file-directory-p tmp-name2))
2849      (write-region "foo" nil (expand-file-name "bla" tmp-name2))
2850      (should (file-exists-p (expand-file-name "bla" tmp-name2)))
2851      (should-error
2852       (delete-directory tmp-name1)
2853       :type 'file-error)
2854      (delete-directory tmp-name1 'recursive)
2855      (should-not (file-directory-p tmp-name1))
2856
2857      ;; Trashing directories works only since Emacs 27.1.  It doesn't
2858      ;; work when `system-move-file-to-trash' is defined (on MS
2859      ;; Windows and macOS), for crypted remote directories and for
2860      ;; ange-ftp.
2861      (when (and (not (fboundp 'system-move-file-to-trash))
2862		 (not (tramp--test-crypt-p)) (not (tramp--test-ftp-p))
2863		 (tramp--test-emacs27-p))
2864	(let ((trash-directory (tramp--test-make-temp-name 'local quoted))
2865	      (delete-by-moving-to-trash t))
2866	  (make-directory trash-directory)
2867	  ;; Delete empty directory.
2868	  (make-directory tmp-name1)
2869	  (should (file-directory-p tmp-name1))
2870	  (delete-directory tmp-name1 nil 'trash)
2871	  (should-not (file-directory-p tmp-name1))
2872	  (should
2873	   (file-exists-p
2874	    (expand-file-name
2875	     (file-name-nondirectory tmp-name1) trash-directory)))
2876	  (delete-directory trash-directory 'recursive)
2877	  (should-not (file-exists-p trash-directory))
2878	  ;; Delete non-empty directory.
2879	  (make-directory tmp-name1)
2880	  (should (file-directory-p tmp-name1))
2881	  (write-region "foo" nil (expand-file-name "bla" tmp-name1))
2882	  (should (file-exists-p (expand-file-name "bla" tmp-name1)))
2883	  (make-directory tmp-name2)
2884	  (should (file-directory-p tmp-name2))
2885	  (write-region "foo" nil (expand-file-name "bla" tmp-name2))
2886	  (should (file-exists-p (expand-file-name "bla" tmp-name2)))
2887	  (should-error
2888	   (delete-directory tmp-name1 nil 'trash)
2889	   ;; tramp-rclone.el and tramp-sshfs.el call the local
2890	   ;; `delete-directory'.  This raises another error.
2891	   :type (if (tramp--test-fuse-p) 'error 'file-error))
2892	  (delete-directory tmp-name1 'recursive 'trash)
2893	  (should-not (file-directory-p tmp-name1))
2894	  (should
2895	   (file-exists-p
2896	    (format
2897	     "%s/%s/bla" trash-directory (file-name-nondirectory tmp-name1))))
2898	  (should
2899	   (file-exists-p
2900	    (format
2901	     "%s/%s/%s/bla" trash-directory (file-name-nondirectory tmp-name1)
2902	     (file-name-nondirectory tmp-name2))))
2903	  (delete-directory trash-directory 'recursive)
2904	  (should-not (file-exists-p trash-directory)))))))
2905
2906(ert-deftest tramp-test15-copy-directory ()
2907  "Check `copy-directory'."
2908  (skip-unless (tramp--test-enabled))
2909  (skip-unless (not (tramp--test-rclone-p)))
2910
2911  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
2912    (let* ((tmp-name1 (tramp--test-make-temp-name nil quoted))
2913	   (tmp-name2 (tramp--test-make-temp-name nil quoted))
2914	   (tmp-name3 (expand-file-name
2915		       (file-name-nondirectory tmp-name1) tmp-name2))
2916	   (tmp-name4 (expand-file-name "foo" tmp-name1))
2917	   (tmp-name5 (expand-file-name "foo" tmp-name2))
2918	   (tmp-name6 (expand-file-name "foo" tmp-name3))
2919	   (tmp-name7 (tramp--test-make-temp-name nil quoted)))
2920
2921      ;; Copy complete directory.
2922      (unwind-protect
2923	  (progn
2924	    (should-error
2925	     (copy-directory tmp-name1 tmp-name2)
2926	     :type 'file-missing)
2927	    ;; Copy empty directory.
2928	    (make-directory tmp-name1)
2929	    (write-region "foo" nil tmp-name4)
2930	    (should (file-directory-p tmp-name1))
2931	    (should (file-exists-p tmp-name4))
2932	    (copy-directory tmp-name1 tmp-name2)
2933	    (should (file-directory-p tmp-name2))
2934	    (should (file-exists-p tmp-name5))
2935	    ;; Target directory does exist already.
2936	    (should-error
2937	     (copy-directory tmp-name1 tmp-name2)
2938	     :type 'file-already-exists)
2939	    (copy-directory tmp-name1 (file-name-as-directory tmp-name2))
2940	    (should (file-directory-p tmp-name3))
2941	    (should (file-exists-p tmp-name6)))
2942
2943	;; Cleanup.
2944	(ignore-errors
2945	  (delete-directory tmp-name1 'recursive)
2946	  (delete-directory tmp-name2 'recursive)))
2947
2948      ;; Copy directory contents.
2949      (unwind-protect
2950	  (progn
2951	    ;; Copy empty directory.
2952	    (make-directory tmp-name1)
2953	    (write-region "foo" nil tmp-name4)
2954	    (should (file-directory-p tmp-name1))
2955	    (should (file-exists-p tmp-name4))
2956	    (copy-directory tmp-name1 tmp-name2 nil 'parents 'contents)
2957	    (should (file-directory-p tmp-name2))
2958	    (should (file-exists-p tmp-name5))
2959	    ;; Target directory does exist already.
2960	    (delete-file tmp-name5)
2961	    (should-not (file-exists-p tmp-name5))
2962	    (copy-directory
2963	     tmp-name1 (file-name-as-directory tmp-name2)
2964	     nil 'parents 'contents)
2965	    (should (file-directory-p tmp-name2))
2966	    (should (file-exists-p tmp-name5))
2967	    (should-not (file-directory-p tmp-name3))
2968	    (should-not (file-exists-p tmp-name6)))
2969
2970	;; Cleanup.
2971	(ignore-errors
2972	  (delete-directory tmp-name1 'recursive)
2973	  (delete-directory tmp-name2 'recursive)))
2974
2975      ;; Copy symlink to directory.  Implemented since Emacs 28.1.
2976      (when (boundp 'copy-directory-create-symlink)
2977	(dolist (copy-directory-create-symlink '(nil t))
2978	  (unwind-protect
2979	      (tramp--test-ignore-make-symbolic-link-error
2980		;; Copy to file name.
2981		(make-directory tmp-name1)
2982		(write-region "foo" nil tmp-name4)
2983		(make-symbolic-link tmp-name1 tmp-name7)
2984		(should (file-directory-p tmp-name1))
2985		(should (file-exists-p tmp-name4))
2986		(should (file-symlink-p tmp-name7))
2987		(copy-directory tmp-name7 tmp-name2)
2988		(if copy-directory-create-symlink
2989		    (should
2990		     (string-equal
2991		      (file-symlink-p tmp-name2) (file-symlink-p tmp-name7)))
2992		  (should (file-directory-p tmp-name2)))
2993		;; Copy to directory name.
2994		(delete-directory tmp-name2 'recursive)
2995		(make-directory tmp-name2)
2996		(should (file-directory-p tmp-name2))
2997		(copy-directory tmp-name7 (file-name-as-directory tmp-name2))
2998		(if copy-directory-create-symlink
2999		    (should
3000		     (string-equal
3001		      (file-symlink-p
3002		       (expand-file-name
3003			(file-name-nondirectory tmp-name7) tmp-name2))
3004		      (file-symlink-p tmp-name7)))
3005		  (should
3006		   (file-directory-p
3007		    (expand-file-name
3008		     (file-name-nondirectory tmp-name7) tmp-name2)))))
3009
3010	    ;; Cleanup.
3011	    (ignore-errors
3012	      (delete-directory tmp-name1 'recursive)
3013	      (delete-directory tmp-name2 'recursive)
3014	      (delete-directory tmp-name7 'recursive))))))))
3015
3016(ert-deftest tramp-test16-directory-files ()
3017  "Check `directory-files'."
3018  (skip-unless (tramp--test-enabled))
3019
3020  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
3021    (let* ((tmp-name1 (tramp--test-make-temp-name nil quoted))
3022	   (tmp-name2 (expand-file-name "bla" tmp-name1))
3023	   (tmp-name3 (expand-file-name "foo" tmp-name1)))
3024      (unwind-protect
3025	  (progn
3026	    (should-error
3027	     (directory-files tmp-name1)
3028	     :type 'file-missing)
3029	    (make-directory tmp-name1)
3030	    (write-region "foo" nil tmp-name2)
3031	    (write-region "bla" nil tmp-name3)
3032	    (should (file-directory-p tmp-name1))
3033	    (should (file-exists-p tmp-name2))
3034	    (should (file-exists-p tmp-name3))
3035	    (should (equal (directory-files tmp-name1) '("." ".." "bla" "foo")))
3036	    (should (equal (directory-files tmp-name1 'full)
3037			   `(,(concat tmp-name1 "/.")
3038			     ,(concat tmp-name1 "/..")
3039			     ,tmp-name2 ,tmp-name3)))
3040	    (should (equal (directory-files
3041			    tmp-name1 nil directory-files-no-dot-files-regexp)
3042			   '("bla" "foo")))
3043	    (should (equal (directory-files
3044			    tmp-name1 'full directory-files-no-dot-files-regexp)
3045			   `(,tmp-name2 ,tmp-name3)))
3046	    ;; Check the COUNT arg.  It exists since Emacs 28.
3047	    (when (tramp--test-emacs28-p)
3048	      (with-no-warnings
3049		(should
3050		 (equal
3051		  (directory-files
3052		   tmp-name1 nil directory-files-no-dot-files-regexp nil 1)
3053		  '("bla"))))))
3054
3055	;; Cleanup.
3056	(ignore-errors (delete-directory tmp-name1 'recursive))))))
3057
3058;; This is not a file name handler test.  But Tramp needed to apply an
3059;; advice for older Emacs versions, so we check that this has been fixed.
3060(ert-deftest tramp-test16-file-expand-wildcards ()
3061  "Check `file-expand-wildcards'."
3062  (skip-unless (tramp--test-enabled))
3063
3064  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
3065    (let* ((tmp-name1 (tramp--test-make-temp-name nil quoted))
3066	   (tmp-name2 (expand-file-name "foo" tmp-name1))
3067	   (tmp-name3 (expand-file-name "bar" tmp-name1))
3068	   (tmp-name4 (expand-file-name "baz" tmp-name1))
3069	   (default-directory tmp-name1))
3070      (unwind-protect
3071	  (progn
3072	    (make-directory tmp-name1)
3073	    (write-region "foo" nil tmp-name2)
3074	    (write-region "bar" nil tmp-name3)
3075	    (write-region "baz" nil tmp-name4)
3076	    (should (file-directory-p tmp-name1))
3077	    (should (file-exists-p tmp-name2))
3078	    (should (file-exists-p tmp-name3))
3079	    (should (file-exists-p tmp-name4))
3080
3081	    ;; `sort' works destructive.
3082	    (should
3083	     (equal (file-expand-wildcards "*")
3084		    (sort (copy-sequence '("foo" "bar" "baz")) 'string<)))
3085	    (should
3086	     (equal (file-expand-wildcards "ba?")
3087		    (sort (copy-sequence '("bar" "baz")) 'string<)))
3088	    (should
3089	     (equal (file-expand-wildcards "ba[rz]")
3090		    (sort (copy-sequence '("bar" "baz")) 'string<)))
3091
3092	    (should
3093	     (equal
3094	      (file-expand-wildcards "*" 'full)
3095	      (sort
3096	       (copy-sequence `(,tmp-name2 ,tmp-name3 ,tmp-name4)) 'string<)))
3097	    (should
3098	     (equal
3099	      (file-expand-wildcards "ba?" 'full)
3100	      (sort (copy-sequence `(,tmp-name3 ,tmp-name4)) 'string<)))
3101	    (should
3102	     (equal
3103	      (file-expand-wildcards "ba[rz]" 'full)
3104	      (sort (copy-sequence `(,tmp-name3 ,tmp-name4)) 'string<)))
3105
3106	    (should
3107	     (equal
3108	      (file-expand-wildcards (concat tmp-name1 "/" "*"))
3109	      (sort
3110	       (copy-sequence `(,tmp-name2 ,tmp-name3 ,tmp-name4)) 'string<)))
3111	    (should
3112	     (equal
3113	      (file-expand-wildcards (concat tmp-name1 "/" "ba?"))
3114	      (sort (copy-sequence `(,tmp-name3 ,tmp-name4)) 'string<)))
3115	    (should
3116	     (equal
3117	      (file-expand-wildcards (concat tmp-name1 "/" "ba[rz]"))
3118	      (sort (copy-sequence `(,tmp-name3 ,tmp-name4)) 'string<))))
3119
3120	;; Cleanup.
3121	(ignore-errors (delete-directory tmp-name1 'recursive))))))
3122
3123(ert-deftest tramp-test17-insert-directory ()
3124  "Check `insert-directory'."
3125  (skip-unless (tramp--test-enabled))
3126  ;; Ange-FTP is very special.  It does not include the header line
3127  ;; (this is performed by `dired').  If FULL is nil, it shows just
3128  ;; one file.  So we refrain from testing.
3129  (skip-unless (not (tramp--test-ange-ftp-p)))
3130  ;; `insert-directory' of crypted remote directories works only since
3131  ;; Emacs 27.1.
3132  (skip-unless (or (not (tramp--test-crypt-p)) (tramp--test-emacs27-p)))
3133
3134  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
3135    (let* ((tmp-name1
3136            (expand-file-name (tramp--test-make-temp-name nil quoted)))
3137	   (tmp-name2 (expand-file-name "foo" tmp-name1))
3138	   ;; We test for the summary line.  Keyword "total" could be localized.
3139	   (process-environment
3140	    (append '("LANG=C" "LANGUAGE=C" "LC_ALL=C") process-environment)))
3141      (unwind-protect
3142	  (progn
3143	    (make-directory tmp-name1)
3144	    (write-region "foo" nil tmp-name2)
3145	    (should (file-directory-p tmp-name1))
3146	    (should (file-exists-p tmp-name2))
3147	    (with-temp-buffer
3148	      (insert-directory tmp-name1 nil)
3149	      (goto-char (point-min))
3150	      (should (looking-at-p (regexp-quote tmp-name1))))
3151	    (with-temp-buffer
3152	      (insert-directory (file-name-as-directory tmp-name1) nil)
3153	      (goto-char (point-min))
3154	      (should
3155               (looking-at-p
3156                (regexp-quote (file-name-as-directory tmp-name1)))))
3157	    (with-temp-buffer
3158	      (insert-directory tmp-name1 "-al")
3159	      (goto-char (point-min))
3160	      (should
3161	       (looking-at-p (format "^.+ %s$" (regexp-quote tmp-name1)))))
3162	    (with-temp-buffer
3163	      (insert-directory (file-name-as-directory tmp-name1) "-al")
3164	      (goto-char (point-min))
3165	      (should
3166	       (looking-at-p (format "^.+ %s/$" (regexp-quote tmp-name1)))))
3167	    (with-temp-buffer
3168	      (insert-directory
3169	       (file-name-as-directory tmp-name1) "-al" nil 'full-directory-p)
3170	      (goto-char (point-min))
3171	      (should
3172	       (looking-at-p
3173		(concat
3174		 ;; There might be a summary line.
3175		 "\\(total.+[[:digit:]]+ ?[kKMGTPEZY]?i?B?\n\\)?"
3176		 ;; We don't know in which order ".", ".." and "foo" appear.
3177		 (format
3178		  "\\(.+ %s\\( ->.+\\)?\n\\)\\{%d\\}"
3179		  (regexp-opt (directory-files tmp-name1))
3180		  (length (directory-files tmp-name1)))))))
3181
3182	    ;; Check error cases.
3183	    (when (and (tramp--test-supports-set-file-modes-p)
3184		       ;; With "sshfs", directories with zero file
3185		       ;; modes are still "accessible".
3186		       (not (tramp--test-sshfs-p))
3187		       ;; A directory is always accessible for user "root".
3188		       (not (zerop (file-attribute-user-id
3189				    (file-attributes tmp-name1)))))
3190	      (set-file-modes tmp-name1 0)
3191	      (with-temp-buffer
3192		(should-error
3193		 (insert-directory tmp-name1 nil)
3194		 :type 'file-error))
3195	      (set-file-modes tmp-name1 #o777))
3196	    (delete-directory tmp-name1 'recursive)
3197	    (with-temp-buffer
3198	      (should-error
3199	       (insert-directory tmp-name1 nil)
3200	       :type 'file-missing)))
3201
3202	;; Cleanup.
3203	(ignore-errors (delete-directory tmp-name1 'recursive))))))
3204
3205(ert-deftest tramp-test17-dired-with-wildcards ()
3206  "Check `dired' with wildcards."
3207  ;; `separate' syntax and IPv6 host name syntax do not work.
3208  (skip-unless (not (string-match-p "\\[" tramp-test-temporary-file-directory)))
3209  (skip-unless (tramp--test-enabled))
3210  (skip-unless (tramp--test-sh-p))
3211  (skip-unless (not (tramp--test-rsync-p)))
3212  ;; Wildcards are not supported in tramp-crypt.el.
3213  (skip-unless (not (tramp--test-crypt-p)))
3214
3215  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
3216    (let* ((tmp-name1
3217	    (expand-file-name (tramp--test-make-temp-name nil quoted)))
3218	   (tmp-name2
3219            (expand-file-name (tramp--test-make-temp-name nil quoted)))
3220	   (tmp-name3 (expand-file-name "foo" tmp-name1))
3221	   (tmp-name4 (expand-file-name "bar" tmp-name2))
3222	   (tramp-test-temporary-file-directory
3223	    (funcall
3224	     (if quoted #'tramp-compat-file-name-quote #'identity)
3225	     tramp-test-temporary-file-directory))
3226	   buffer)
3227      (unwind-protect
3228	  (progn
3229	    (make-directory tmp-name1)
3230	    (write-region "foo" nil tmp-name3)
3231	    (should (file-directory-p tmp-name1))
3232	    (should (file-exists-p tmp-name3))
3233	    (make-directory tmp-name2)
3234	    (write-region "foo" nil tmp-name4)
3235	    (should (file-directory-p tmp-name2))
3236	    (should (file-exists-p tmp-name4))
3237
3238	    ;; Check for expanded directory names.
3239	    (with-current-buffer
3240		(setq buffer
3241		      (dired-noselect
3242		       (expand-file-name
3243			"tramp-test*" tramp-test-temporary-file-directory)))
3244	      (goto-char (point-min))
3245	      (should
3246	       (re-search-forward
3247		(regexp-quote
3248		 (file-relative-name
3249		  tmp-name1 tramp-test-temporary-file-directory))))
3250	      (goto-char (point-min))
3251	      (should
3252	       (re-search-forward
3253		(regexp-quote
3254		 (file-relative-name
3255		  tmp-name2 tramp-test-temporary-file-directory)))))
3256	    (kill-buffer buffer)
3257
3258	    ;; Check for expanded directory and file names.
3259	    (with-current-buffer
3260		(setq buffer
3261		      (dired-noselect
3262		       (expand-file-name
3263			"tramp-test*/*" tramp-test-temporary-file-directory)))
3264	      (goto-char (point-min))
3265	      (should
3266	       (re-search-forward
3267		(regexp-quote
3268		 (file-relative-name
3269		  tmp-name3 tramp-test-temporary-file-directory))))
3270	      (goto-char (point-min))
3271	      (should
3272	       (re-search-forward
3273		(regexp-quote
3274		 (file-relative-name
3275		  tmp-name4
3276		  tramp-test-temporary-file-directory)))))
3277	    (kill-buffer buffer)
3278
3279	    ;; Check for special characters.
3280	    (setq tmp-name3 (expand-file-name "*?" tmp-name1))
3281	    (setq tmp-name4 (expand-file-name "[a-z0-9]" tmp-name2))
3282	    (write-region "foo" nil tmp-name3)
3283	    (should (file-exists-p tmp-name3))
3284	    (write-region "foo" nil tmp-name4)
3285	    (should (file-exists-p tmp-name4))
3286
3287	    (with-current-buffer
3288		(setq buffer
3289		      (dired-noselect
3290		       (expand-file-name
3291			"tramp-test*/*" tramp-test-temporary-file-directory)))
3292	      (goto-char (point-min))
3293	      (should
3294	       (re-search-forward
3295		(regexp-quote
3296		 (file-relative-name
3297		  tmp-name3 tramp-test-temporary-file-directory))))
3298	      (goto-char (point-min))
3299	      (should
3300	       (re-search-forward
3301		(regexp-quote
3302		 (file-relative-name
3303		  tmp-name4
3304		  tramp-test-temporary-file-directory)))))
3305	    (kill-buffer buffer))
3306
3307	;; Cleanup.
3308	(ignore-errors (kill-buffer buffer))
3309	(ignore-errors (delete-directory tmp-name1 'recursive))
3310	(ignore-errors (delete-directory tmp-name2 'recursive))))))
3311
3312;; The following test is inspired by Bug#45691.
3313(ert-deftest tramp-test17-insert-directory-one-file ()
3314  "Check `insert-directory' inside directory listing."
3315  (skip-unless (tramp--test-enabled))
3316  ;; Relative file names in dired are not supported in tramp-crypt.el.
3317  (skip-unless (not (tramp--test-crypt-p)))
3318
3319  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
3320    (let* ((tmp-name1
3321	    (expand-file-name (tramp--test-make-temp-name nil quoted)))
3322	   (tmp-name2 (expand-file-name "foo" tmp-name1))
3323	   (tmp-name3 (expand-file-name "bar" tmp-name1))
3324	   (dired-copy-preserve-time t)
3325	   (dired-recursive-copies 'top)
3326	   dired-copy-dereference
3327	   buffer)
3328      (unwind-protect
3329	  (progn
3330	    (make-directory tmp-name1)
3331	    (write-region "foo" nil tmp-name2)
3332	    (should (file-directory-p tmp-name1))
3333	    (should (file-exists-p tmp-name2))
3334
3335	    ;; Check, that `insert-directory' works properly.
3336	    (with-current-buffer
3337		(setq buffer (dired-noselect tmp-name1 "--dired -al"))
3338	      (read-only-mode -1)
3339	      (goto-char (point-min))
3340	      (while (not (or (eobp)
3341			      (string-equal
3342			       (dired-get-filename 'no-dir 'no-error)
3343			       (file-name-nondirectory tmp-name2))))
3344		(forward-line 1))
3345	      (should-not (eobp))
3346	      (copy-file tmp-name2 tmp-name3)
3347	      (insert-directory
3348	       (file-name-nondirectory tmp-name3) "--dired -al -d")
3349	      ;; Point shall still be the recent file.
3350	      (should
3351	       (string-equal
3352		(dired-get-filename 'no-dir 'no-error)
3353		(file-name-nondirectory tmp-name2)))
3354	      (should-not (re-search-forward "dired" nil t))
3355	      ;; The copied file has been inserted the line before.
3356	      (forward-line -1)
3357	      (should
3358	       (string-equal
3359		(dired-get-filename 'no-dir 'no-error)
3360		(file-name-nondirectory tmp-name3))))
3361	    (kill-buffer buffer))
3362
3363	;; Cleanup.
3364	(ignore-errors (kill-buffer buffer))
3365	(ignore-errors (delete-directory tmp-name1 'recursive))))))
3366
3367(ert-deftest tramp-test18-file-attributes ()
3368  "Check `file-attributes'.
3369This tests also `access-file', `file-readable-p',
3370`file-regular-p' and `file-ownership-preserved-p'."
3371  (skip-unless (tramp--test-enabled))
3372
3373  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
3374    ;; We must use `file-truename' for the temporary directory,
3375    ;; because it could be located on a symlinked directory.  This
3376    ;; would let the test fail.
3377    (let* ((tramp-test-temporary-file-directory
3378	    (file-truename tramp-test-temporary-file-directory))
3379	   (tmp-name1 (tramp--test-make-temp-name nil quoted))
3380	   (tmp-name2 (tramp--test-make-temp-name nil quoted))
3381	   ;; File name with "//".
3382	   (tmp-name3
3383	    (format
3384	     "%s%s"
3385	     (file-remote-p tmp-name1)
3386	     (replace-regexp-in-string
3387	      "/" "//" (file-remote-p tmp-name1 'localname))))
3388	   ;; `file-ownership-preserved-p' is implemented only in tramp-sh.el.
3389	   (test-file-ownership-preserved-p (tramp--test-sh-p))
3390	   attr)
3391      (unwind-protect
3392	  (progn
3393	    ;; A sticky bit could damage the `file-ownership-preserved-p' test.
3394	    (when
3395		(and test-file-ownership-preserved-p
3396		     (zerop (logand
3397			     #o1000
3398			     (file-modes tramp-test-temporary-file-directory))))
3399	      (write-region "foo" nil tmp-name1)
3400	      (setq test-file-ownership-preserved-p
3401		    (= (file-attribute-group-id (file-attributes tmp-name1))
3402		       (tramp-get-remote-gid tramp-test-vec 'integer)))
3403	      (delete-file tmp-name1))
3404
3405	    (when (tramp--test-supports-set-file-modes-p)
3406	      (write-region "foo" nil tmp-name1)
3407	      ;; A file is always accessible for user "root".
3408	      (unless
3409		  (zerop (file-attribute-user-id (file-attributes tmp-name1)))
3410		(set-file-modes tmp-name1 0)
3411		(should-error
3412		 (access-file tmp-name1 "error")
3413		 :type tramp-permission-denied)
3414		(set-file-modes tmp-name1 #o777))
3415	      (delete-file tmp-name1))
3416	    (should-error
3417	     (access-file tmp-name1 "error")
3418	     :type 'file-missing)
3419
3420	    ;; `file-ownership-preserved-p' should return t for
3421	    ;; non-existing files.
3422	    (when test-file-ownership-preserved-p
3423	      (should (file-ownership-preserved-p tmp-name1 'group)))
3424	    (write-region "foo" nil tmp-name1)
3425	    (should (file-exists-p tmp-name1))
3426	    (should (file-readable-p tmp-name1))
3427	    (should (file-regular-p tmp-name1))
3428	    (should-not (access-file tmp-name1 "error"))
3429	    (when test-file-ownership-preserved-p
3430	      (should (file-ownership-preserved-p tmp-name1 'group)))
3431
3432	    ;; We do not test inodes and device numbers.
3433	    (setq attr (file-attributes tmp-name1))
3434	    (should (consp attr))
3435	    (should (null (file-attribute-type attr)))
3436	    (should (numberp (file-attribute-link-number attr)))
3437	    (should (numberp (file-attribute-user-id attr)))
3438	    (should (numberp (file-attribute-group-id attr)))
3439	    (should
3440	     (stringp (current-time-string (file-attribute-access-time attr))))
3441	    (should
3442	     (stringp
3443	      (current-time-string (file-attribute-modification-time attr))))
3444	    (should
3445	     (stringp
3446	      (current-time-string (file-attribute-status-change-time attr))))
3447	    (should (numberp (file-attribute-size attr)))
3448	    (should (stringp (file-attribute-modes attr)))
3449
3450	    (setq attr (file-attributes tmp-name1 'string))
3451	    (should (stringp (file-attribute-user-id attr)))
3452	    (should (stringp (file-attribute-group-id attr)))
3453
3454	    (tramp--test-ignore-make-symbolic-link-error
3455	      (should-error
3456	       (access-file tmp-name2 "error")
3457	       :type 'file-missing)
3458	      (when test-file-ownership-preserved-p
3459		(should (file-ownership-preserved-p tmp-name2 'group)))
3460	      (make-symbolic-link tmp-name1 tmp-name2)
3461	      (should (file-exists-p tmp-name2))
3462	      (should (file-symlink-p tmp-name2))
3463	      (should-not (access-file tmp-name2 "error"))
3464	      (when test-file-ownership-preserved-p
3465		(should (file-ownership-preserved-p tmp-name2 'group)))
3466	      (setq attr (file-attributes tmp-name2))
3467	      (should
3468	       (string-equal
3469		(funcall
3470		 (if quoted #'tramp-compat-file-name-quote #'identity)
3471		 (file-attribute-type attr))
3472		(file-remote-p (file-truename tmp-name1) 'localname)))
3473	      (delete-file tmp-name2))
3474
3475	    ;; Check, that "//" in symlinks are handled properly.
3476	    (with-temp-buffer
3477	      (let ((default-directory tramp-test-temporary-file-directory))
3478		(shell-command
3479		 (format
3480		  "ln -s %s %s"
3481		  (tramp-file-name-localname
3482		   (tramp-dissect-file-name tmp-name3))
3483		  (tramp-file-name-localname
3484		   (tramp-dissect-file-name tmp-name2)))
3485		 t)))
3486	    (when (file-symlink-p tmp-name2)
3487	      (setq attr (file-attributes tmp-name2))
3488	      (should
3489	       (string-equal
3490		(file-attribute-type attr)
3491		(tramp-file-name-localname
3492		 (tramp-dissect-file-name tmp-name3))))
3493	      (delete-file tmp-name2))
3494
3495	    (when test-file-ownership-preserved-p
3496	      (should (file-ownership-preserved-p tmp-name1 'group)))
3497	    (delete-file tmp-name1)
3498	    (make-directory tmp-name1)
3499	    (should (file-exists-p tmp-name1))
3500	    (should (file-readable-p tmp-name1))
3501	    (should-not (file-regular-p tmp-name1))
3502	    (should-not (access-file tmp-name1 ""))
3503	    (when test-file-ownership-preserved-p
3504	      (should (file-ownership-preserved-p tmp-name1 'group)))
3505	    (setq attr (file-attributes tmp-name1))
3506	    (should (eq (file-attribute-type attr) t)))
3507
3508	;; Cleanup.
3509	(ignore-errors (delete-directory tmp-name1))
3510	(ignore-errors (delete-file tmp-name1))
3511	(ignore-errors (delete-file tmp-name2))))))
3512
3513(defvar tramp--test-start-time nil
3514  "Keep the start time of the current test, a float number.")
3515
3516(defsubst tramp--test-file-attributes-equal-p (attr1 attr2)
3517  "Check, whether file attributes ATTR1 and ATTR2 are equal.
3518They might differ only in time attributes or directory size."
3519  (let ((attr1 (copy-sequence attr1))
3520	(attr2 (copy-sequence attr2))
3521	(start-time (- tramp--test-start-time 10)))
3522    ;; Link number.  For directories, it includes the number of
3523    ;; subdirectories.  Set it to 1.
3524    (when (eq (file-attribute-type attr1) t)
3525      (setcar (nthcdr 1 attr1) 1))
3526    (when (eq (file-attribute-type attr2) t)
3527      (setcar (nthcdr 1 attr2) 1))
3528    ;; Access time.
3529    (setcar (nthcdr 4 attr1) tramp-time-dont-know)
3530    (setcar (nthcdr 4 attr2) tramp-time-dont-know)
3531    ;; Modification time.  If any of the time values is "don't know",
3532    ;; we cannot compare, and we normalize the time stamps.  If the
3533    ;; time value is newer than the test start time, normalize it,
3534    ;; because due to caching the time stamps could differ slightly (a
3535    ;; few seconds).  We use a test start time minus 10 seconds, in
3536    ;; order to compensate a possible timestamp resolution higher than
3537    ;; a second on the remote machine.
3538    (when (or (tramp-compat-time-equal-p
3539	       (file-attribute-modification-time attr1) tramp-time-dont-know)
3540	      (tramp-compat-time-equal-p
3541	       (file-attribute-modification-time attr2) tramp-time-dont-know))
3542      (setcar (nthcdr 5 attr1) tramp-time-dont-know)
3543      (setcar (nthcdr 5 attr2) tramp-time-dont-know))
3544    (when (< start-time
3545	     (float-time (file-attribute-modification-time attr1)))
3546      (setcar (nthcdr 5 attr1) tramp-time-dont-know))
3547    (when (< start-time
3548	     (float-time (file-attribute-modification-time attr2)))
3549      (setcar (nthcdr 5 attr2) tramp-time-dont-know))
3550    ;; Status change time.  Ditto.
3551    (when (or (tramp-compat-time-equal-p
3552	       (file-attribute-status-change-time attr1) tramp-time-dont-know)
3553	      (tramp-compat-time-equal-p
3554	       (file-attribute-status-change-time attr2) tramp-time-dont-know))
3555      (setcar (nthcdr 6 attr1) tramp-time-dont-know)
3556      (setcar (nthcdr 6 attr2) tramp-time-dont-know))
3557    (when (< start-time (float-time (file-attribute-status-change-time attr1)))
3558      (setcar (nthcdr 6 attr1) tramp-time-dont-know))
3559    (when (< start-time (float-time (file-attribute-status-change-time attr2)))
3560      (setcar (nthcdr 6 attr2) tramp-time-dont-know))
3561    ;; Size.  Set it to 0 for directories, because it might have
3562    ;; changed.  For example the upper directory "../".
3563    (when (eq (file-attribute-type attr1) t)
3564      (setcar (nthcdr 7 attr1) 0))
3565    (when (eq (file-attribute-type attr2) t)
3566      (setcar (nthcdr 7 attr2) 0))
3567    ;; The check.
3568    (unless (equal attr1 attr2) (tramp--test-message "%S\n%S" attr1 attr2))
3569    (equal attr1 attr2)))
3570
3571;; This isn't 100% correct, but better than no explainer at all.
3572(put #'tramp--test-file-attributes-equal-p 'ert-explainer #'ert--explain-equal)
3573
3574(ert-deftest tramp-test19-directory-files-and-attributes ()
3575  "Check `directory-files-and-attributes'."
3576  (skip-unless (tramp--test-enabled))
3577
3578  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
3579    ;; `directory-files-and-attributes' contains also values for
3580    ;; "../".  Ensure that this doesn't change during tests, for
3581    ;; example due to handling temporary files.
3582    (let* ((tmp-name1 (tramp--test-make-temp-name nil quoted))
3583	   (tmp-name2 (expand-file-name "bla" tmp-name1))
3584	   attr)
3585      (unwind-protect
3586	  (progn
3587	    (should-error
3588	     (directory-files-and-attributes tmp-name1)
3589	     :type 'file-missing)
3590	    (make-directory tmp-name1)
3591	    (should (file-directory-p tmp-name1))
3592	    (setq tramp--test-start-time
3593		  (float-time
3594		   (file-attribute-modification-time
3595		    (file-attributes tmp-name1))))
3596	    (make-directory tmp-name2)
3597	    (should (file-directory-p tmp-name2))
3598	    (write-region "foo" nil (expand-file-name "foo" tmp-name2))
3599	    (write-region "bar" nil (expand-file-name "bar" tmp-name2))
3600	    (write-region "boz" nil (expand-file-name "boz" tmp-name2))
3601
3602	    (setq attr (directory-files-and-attributes tmp-name2))
3603	    (should (consp attr))
3604	    (dolist (elt attr)
3605	      (should
3606	       (tramp--test-file-attributes-equal-p
3607		(file-attributes (expand-file-name (car elt) tmp-name2))
3608		(cdr elt))))
3609
3610	    (setq attr (directory-files-and-attributes tmp-name2 'full))
3611	    (should (consp attr))
3612	    (dolist (elt attr)
3613	      (should
3614	       (tramp--test-file-attributes-equal-p
3615		(file-attributes (car elt)) (cdr elt))))
3616
3617	    (setq attr (directory-files-and-attributes tmp-name2 nil "\\`b"))
3618	    (should (equal (mapcar #'car attr) '("bar" "boz")))
3619
3620	    ;; Check the COUNT arg.  It exists since Emacs 28.
3621	    (when (tramp--test-emacs28-p)
3622	      (with-no-warnings
3623		(setq attr (directory-files-and-attributes
3624			    tmp-name2 nil "\\`b" nil nil 1))
3625		(should (equal (mapcar #'car attr) '("bar"))))))
3626
3627	;; Cleanup.
3628	(ignore-errors (delete-directory tmp-name1 'recursive))))))
3629
3630(ert-deftest tramp-test20-file-modes ()
3631  "Check `file-modes'.
3632This tests also `file-executable-p', `file-writable-p' and `set-file-modes'."
3633  (skip-unless (tramp--test-enabled))
3634  (skip-unless (tramp--test-supports-set-file-modes-p))
3635
3636  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
3637    (let ((tmp-name1 (tramp--test-make-temp-name nil quoted))
3638	  (tmp-name2 (tramp--test-make-temp-name nil quoted)))
3639
3640      (unwind-protect
3641	  (progn
3642	    (write-region "foo" nil tmp-name1)
3643	    (should (file-exists-p tmp-name1))
3644	    (set-file-modes tmp-name1 #o777)
3645	    (should (= (file-modes tmp-name1) #o777))
3646	    (should (file-executable-p tmp-name1))
3647	    (should (file-writable-p tmp-name1))
3648	    (set-file-modes tmp-name1 #o444)
3649	    (should (= (file-modes tmp-name1) #o444))
3650	    (should-not (file-executable-p tmp-name1))
3651	    ;; A file is always writable for user "root".
3652	    (unless (zerop (file-attribute-user-id (file-attributes tmp-name1)))
3653	      (should-not (file-writable-p tmp-name1)))
3654	    ;; Check the NOFOLLOW arg.  It exists since Emacs 28.  For
3655	    ;; regular files, there shouldn't be a difference.
3656	    (when (tramp--test-emacs28-p)
3657	      (with-no-warnings
3658		(set-file-modes tmp-name1 #o222 'nofollow)
3659		(should (= (file-modes tmp-name1 'nofollow) #o222)))))
3660
3661	;; Cleanup.
3662	(ignore-errors (delete-file tmp-name1)))
3663
3664      ;; Check the NOFOLLOW arg.  It exists since Emacs 28.  It is
3665      ;; implemented for tramp-gvfs.el and tramp-sh.el.  However,
3666      ;; tramp-gvfs,el does not support creating symbolic links.  And
3667      ;; in tramp-sh.el, we must ensure that the remote chmod command
3668      ;; supports the "-h" argument.
3669      (when (and (tramp--test-emacs28-p) (tramp--test-sh-p)
3670		 (tramp-get-remote-chmod-h tramp-test-vec))
3671	(unwind-protect
3672	    (with-no-warnings
3673	      (write-region "foo" nil tmp-name1)
3674	      (should (file-exists-p tmp-name1))
3675	      (make-symbolic-link tmp-name1 tmp-name2)
3676	      (should
3677	       (string-equal
3678		(funcall
3679		 (if quoted #'tramp-compat-file-name-unquote #'identity)
3680		 (file-remote-p tmp-name1 'localname))
3681		(file-symlink-p tmp-name2)))
3682	      ;; Both report the modes of `tmp-name1'.
3683	      (should
3684	       (= (file-modes tmp-name1) (file-modes tmp-name2)))
3685	      ;; `tmp-name1' is a regular file.  NOFOLLOW doesn't matter.
3686	      (should
3687	       (= (file-modes tmp-name1) (file-modes tmp-name1 'nofollow)))
3688	      ;; `tmp-name2' is a symbolic link.  It has different permissions.
3689	      (should-not
3690	       (= (file-modes tmp-name2) (file-modes tmp-name2 'nofollow)))
3691	      (should-not
3692	       (= (file-modes tmp-name1 'nofollow)
3693		  (file-modes tmp-name2 'nofollow)))
3694	      ;; Change permissions.
3695	      (set-file-modes tmp-name1 #o200)
3696	      (set-file-modes tmp-name2 #o200)
3697	      (should
3698	       (= (file-modes tmp-name1) (file-modes tmp-name2) #o200))
3699	      ;; Change permissions with NOFOLLOW.
3700	      (set-file-modes tmp-name1 #o300 'nofollow)
3701	      (set-file-modes tmp-name2 #o300 'nofollow)
3702	      (should
3703	       (= (file-modes tmp-name1 'nofollow)
3704		  (file-modes tmp-name2 'nofollow)))
3705	      (should-not (= (file-modes tmp-name1) (file-modes tmp-name2))))
3706
3707	  ;; Cleanup.
3708	  (ignore-errors (delete-file tmp-name1))
3709	  (ignore-errors (delete-file tmp-name2)))))))
3710
3711;; Method "smb" could run into "NT_STATUS_REVISION_MISMATCH" error.
3712(defmacro tramp--test-ignore-add-name-to-file-error (&rest body)
3713  "Run BODY, ignoring \"error with add-name-to-file\" file error."
3714  (declare (indent defun) (debug (body)))
3715  `(condition-case err
3716       (progn ,@body)
3717     (file-error
3718      (unless (string-match-p "^error with add-name-to-file"
3719			      (error-message-string err))
3720	(signal (car err) (cdr err))))))
3721
3722(ert-deftest tramp-test21-file-links ()
3723  "Check `file-symlink-p'.
3724This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'."
3725  (skip-unless (tramp--test-enabled))
3726
3727  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
3728    ;; We must use `file-truename' for the temporary directory,
3729    ;; because it could be located on a symlinked directory.  This
3730    ;; would let the test fail.
3731    (let* ((tramp-test-temporary-file-directory
3732	    (file-truename tramp-test-temporary-file-directory))
3733	   (tmp-name1 (tramp--test-make-temp-name nil quoted))
3734	   (tmp-name2 (tramp--test-make-temp-name nil quoted))
3735	   (tmp-name3 (tramp--test-make-temp-name 'local quoted))
3736	   (tmp-name4 (tramp--test-make-temp-name nil quoted))
3737	   (tmp-name5
3738	    (expand-file-name (file-name-nondirectory tmp-name1) tmp-name4))
3739	   (tmp-name6 (tramp--test-make-temp-name nil quoted)))
3740      ;; Check `make-symbolic-link'.
3741      (unwind-protect
3742	  (tramp--test-ignore-make-symbolic-link-error
3743	    (write-region "foo" nil tmp-name1)
3744	    (should (file-exists-p tmp-name1))
3745	    (make-symbolic-link tmp-name1 tmp-name2)
3746	    (should
3747	     (string-equal
3748	      (funcall
3749	       (if quoted #'tramp-compat-file-name-unquote #'identity)
3750	       (file-remote-p tmp-name1 'localname))
3751	      (file-symlink-p tmp-name2)))
3752	    (when (tramp--test-expensive-test-p)
3753	      (should-error
3754	       (make-symbolic-link tmp-name1 tmp-name2)
3755	       :type 'file-already-exists))
3756	    (when (tramp--test-expensive-test-p)
3757	      ;; A number means interactive case.
3758	      (cl-letf (((symbol-function #'yes-or-no-p) #'ignore))
3759		(should-error
3760		 (make-symbolic-link tmp-name1 tmp-name2 0)
3761		 :type 'file-already-exists)))
3762	    (cl-letf (((symbol-function #'yes-or-no-p) #'tramp--test-always))
3763	      (make-symbolic-link tmp-name1 tmp-name2 0)
3764	      (should
3765	       (string-equal
3766		(funcall
3767		 (if quoted #'tramp-compat-file-name-unquote #'identity)
3768		 (file-remote-p tmp-name1 'localname))
3769		(file-symlink-p tmp-name2))))
3770	    (make-symbolic-link tmp-name1 tmp-name2 'ok-if-already-exists)
3771	    (should
3772	     (string-equal
3773	      (funcall
3774	       (if quoted #'tramp-compat-file-name-unquote #'identity)
3775	       (file-remote-p tmp-name1 'localname))
3776	      (file-symlink-p tmp-name2)))
3777	    ;; If we use the local part of `tmp-name1', it shall still work.
3778	    (make-symbolic-link
3779	     (file-remote-p tmp-name1 'localname)
3780	     tmp-name2 'ok-if-already-exists)
3781	    (should
3782	     (string-equal
3783	      (funcall
3784	       (if quoted #'tramp-compat-file-name-unquote #'identity)
3785	       (file-remote-p tmp-name1 'localname))
3786	      (file-symlink-p tmp-name2)))
3787	    ;; `tmp-name3' is a local file name.  Therefore, the link
3788	    ;; target remains unchanged, even if quoted.
3789	    ;; `make-symbolic-link' might not be permitted on w32 systems.
3790	    (unless (tramp--test-windows-nt-p)
3791	      (make-symbolic-link tmp-name1 tmp-name3)
3792	      (should
3793	       (string-equal tmp-name1 (file-symlink-p tmp-name3))))
3794	    ;; Check directory as newname.
3795	    (make-directory tmp-name4)
3796	    (when (tramp--test-expensive-test-p)
3797	      (should-error
3798	       (make-symbolic-link tmp-name1 tmp-name4)
3799	       :type 'file-already-exists))
3800	    (make-symbolic-link tmp-name1 (file-name-as-directory tmp-name4))
3801	    (should
3802	     (string-equal
3803	      (funcall
3804	       (if quoted #'tramp-compat-file-name-unquote #'identity)
3805	       (file-remote-p tmp-name1 'localname))
3806	      (file-symlink-p tmp-name5)))
3807	    ;; Check, that files in symlinked directories still work.
3808	    (make-symbolic-link tmp-name4 tmp-name6)
3809	    (write-region "foo" nil (expand-file-name "foo" tmp-name6))
3810	    (delete-file (expand-file-name "foo" tmp-name6))
3811	    (should-not (file-exists-p (expand-file-name "foo" tmp-name4)))
3812	    (should-not (file-exists-p (expand-file-name "foo" tmp-name6))))
3813
3814	;; Cleanup.
3815	(ignore-errors (delete-file tmp-name1))
3816	(ignore-errors (delete-file tmp-name2))
3817	(ignore-errors (delete-file tmp-name3))
3818	(ignore-errors (delete-file tmp-name5))
3819	(ignore-errors (delete-file tmp-name6))
3820	(ignore-errors (delete-directory tmp-name4 'recursive)))
3821
3822      ;; Check `add-name-to-file'.
3823      (unwind-protect
3824	  (when (tramp--test-expensive-test-p)
3825	    (tramp--test-ignore-add-name-to-file-error
3826	     (write-region "foo" nil tmp-name1)
3827	     (should (file-exists-p tmp-name1))
3828	     (add-name-to-file tmp-name1 tmp-name2)
3829	     (should (file-regular-p tmp-name2))
3830	     (should-error
3831	      (add-name-to-file tmp-name1 tmp-name2)
3832	      :type 'file-already-exists)
3833	     ;; A number means interactive case.
3834	     (cl-letf (((symbol-function #'yes-or-no-p) #'ignore))
3835	       (should-error
3836		(add-name-to-file tmp-name1 tmp-name2 0)
3837		:type 'file-already-exists))
3838	     (cl-letf (((symbol-function #'yes-or-no-p) #'tramp--test-always))
3839	       (add-name-to-file tmp-name1 tmp-name2 0)
3840	       (should (file-regular-p tmp-name2)))
3841	     (add-name-to-file tmp-name1 tmp-name2 'ok-if-already-exists)
3842	     (should-not (file-symlink-p tmp-name2))
3843	     (should (file-regular-p tmp-name2))
3844	     ;; `tmp-name3' is a local file name.
3845	     (should-error
3846	      (add-name-to-file tmp-name1 tmp-name3)
3847	      :type 'file-error)
3848	     ;; Check directory as newname.
3849	     (make-directory tmp-name4)
3850	     (should-error
3851	      (add-name-to-file tmp-name1 tmp-name4)
3852	      :type 'file-already-exists)
3853	     (add-name-to-file tmp-name1 (file-name-as-directory tmp-name4))
3854	     (should
3855	      (file-regular-p
3856	       (expand-file-name
3857		(file-name-nondirectory tmp-name1) tmp-name4)))))
3858
3859	;; Cleanup.
3860	(ignore-errors
3861	  (delete-file tmp-name1)
3862	  (delete-file tmp-name2)
3863	  (delete-directory tmp-name4 'recursive)))
3864
3865      ;; Check `file-truename'.
3866      (unwind-protect
3867	  (tramp--test-ignore-make-symbolic-link-error
3868	    (write-region "foo" nil tmp-name1)
3869	    (should (file-exists-p tmp-name1))
3870	    (should (string-equal tmp-name1 (file-truename tmp-name1)))
3871	    (make-symbolic-link tmp-name1 tmp-name2)
3872	    (should (file-symlink-p tmp-name2))
3873	    (should-not (string-equal tmp-name2 (file-truename tmp-name2)))
3874	    (should
3875	     (string-equal (file-truename tmp-name1) (file-truename tmp-name2)))
3876	    (should (file-equal-p tmp-name1 tmp-name2))
3877	    ;; Check relative symlink file name.
3878	    (delete-file tmp-name2)
3879	    (let ((default-directory tramp-test-temporary-file-directory))
3880	      (make-symbolic-link (file-name-nondirectory tmp-name1) tmp-name2))
3881	    (should (file-symlink-p tmp-name2))
3882	    (should-not (string-equal tmp-name2 (file-truename tmp-name2)))
3883	    (should
3884	     (string-equal (file-truename tmp-name1) (file-truename tmp-name2)))
3885	    (should (file-equal-p tmp-name1 tmp-name2))
3886	    ;; Symbolic links could look like a remote file name.
3887	    ;; They must be quoted then.
3888	    (let ((penguin
3889		   (if (eq tramp-syntax 'separate)
3890		       "/[penguin/motd]" "/penguin:motd:")))
3891	      (delete-file tmp-name2)
3892	      (make-symbolic-link
3893	       (funcall
3894		(if quoted #'tramp-compat-file-name-unquote #'identity) penguin)
3895	       tmp-name2)
3896	      (should (file-symlink-p tmp-name2))
3897	      (should
3898	       (string-equal
3899		(file-truename tmp-name2)
3900		(tramp-compat-file-name-quote
3901		 (concat (file-remote-p tmp-name2) penguin)))))
3902	    ;; `tmp-name3' is a local file name.
3903	    ;; `make-symbolic-link' might not be permitted on w32 systems.
3904	    (unless (tramp--test-windows-nt-p)
3905	      (make-symbolic-link tmp-name1 tmp-name3)
3906	      (should (file-symlink-p tmp-name3))
3907              (should-not (string-equal tmp-name3 (file-truename tmp-name3)))
3908	      ;; `file-truename' returns a quoted file name for `tmp-name3'.
3909	      ;; We must unquote it.
3910	      (should
3911	       (string-equal
3912		(file-truename tmp-name1)
3913		(tramp-compat-file-name-unquote (file-truename tmp-name3))))))
3914
3915	;; Cleanup.
3916	(ignore-errors
3917	  (delete-file tmp-name1)
3918	  (delete-file tmp-name2)
3919	  (delete-file tmp-name3)))
3920
3921      ;; Symbolic links could be nested.
3922      (unwind-protect
3923	  (tramp--test-ignore-make-symbolic-link-error
3924	    (make-directory tmp-name1)
3925	    (should (file-directory-p tmp-name1))
3926	    (let* ((tramp-test-temporary-file-directory
3927		    (file-truename tmp-name1))
3928		   (tmp-name2 (tramp--test-make-temp-name nil quoted))
3929		   (tmp-name3 tmp-name2)
3930		   (number-nesting 15))
3931	      (dotimes (_ number-nesting)
3932		(make-symbolic-link
3933		 tmp-name3
3934		 (setq tmp-name3 (tramp--test-make-temp-name nil quoted))))
3935	      (should
3936	       (string-equal
3937		(file-truename tmp-name2)
3938		(file-truename tmp-name3)))
3939	      (when (tramp--test-expensive-test-p)
3940		(should-error
3941		 (with-temp-buffer (insert-file-contents tmp-name2))
3942		 :type 'file-missing))
3943	      (when (tramp--test-expensive-test-p)
3944		(should-error
3945		 (with-temp-buffer (insert-file-contents tmp-name3))
3946		 :type 'file-missing))
3947	      ;; `directory-files' does not show symlinks to
3948	      ;; non-existing targets in the "smb" case.  So we remove
3949	      ;; the symlinks manually.
3950	      (while (stringp (setq tmp-name2 (file-symlink-p tmp-name3)))
3951		(delete-file tmp-name3)
3952		(setq tmp-name3 (concat (file-remote-p tmp-name3) tmp-name2)))))
3953
3954	;; Cleanup.
3955	(ignore-errors
3956	  (delete-file tmp-name3)
3957	  (delete-directory tmp-name1 'recursive)))
3958
3959      ;; Detect cyclic symbolic links.
3960      (unwind-protect
3961	  (when (tramp--test-expensive-test-p)
3962	    (tramp--test-ignore-make-symbolic-link-error
3963	     (make-symbolic-link tmp-name2 tmp-name1)
3964	     (should (file-symlink-p tmp-name1))
3965	     (if (tramp--test-smb-p)
3966		 ;; The symlink command of "smbclient" detects the
3967		 ;; cycle already.
3968		 (should-error
3969		  (make-symbolic-link tmp-name1 tmp-name2)
3970		  :type 'file-error)
3971	       (make-symbolic-link tmp-name1 tmp-name2)
3972	       (should (file-symlink-p tmp-name2))
3973	       (should-error
3974		(file-truename tmp-name1)
3975		:type 'file-error))))
3976
3977	;; Cleanup.
3978	(ignore-errors
3979	  (delete-file tmp-name1)
3980	  (delete-file tmp-name2)))
3981
3982      ;; `file-truename' shall preserve trailing slash of directories.
3983      (let* ((dir1
3984	      (directory-file-name
3985	       (funcall
3986		(if quoted #'tramp-compat-file-name-quote #'identity)
3987		tramp-test-temporary-file-directory)))
3988	     (dir2 (file-name-as-directory dir1)))
3989	(should (string-equal (file-truename dir1) (expand-file-name dir1)))
3990	(should (string-equal (file-truename dir2) (expand-file-name dir2)))))))
3991
3992(ert-deftest tramp-test22-file-times ()
3993  "Check `set-file-times' and `file-newer-than-file-p'."
3994  (skip-unless (tramp--test-enabled))
3995  (skip-unless
3996   (or (tramp--test-adb-p) (tramp--test-gvfs-p)
3997       (tramp--test-sh-p) (tramp--test-sudoedit-p)))
3998
3999  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
4000    (let ((tmp-name1 (tramp--test-make-temp-name nil quoted))
4001	  (tmp-name2 (tramp--test-make-temp-name nil quoted))
4002	  (tmp-name3 (tramp--test-make-temp-name nil quoted)))
4003      (unwind-protect
4004	  (progn
4005	    (write-region "foo" nil tmp-name1)
4006	    (should (file-exists-p tmp-name1))
4007	    (should (consp (file-attribute-modification-time
4008			    (file-attributes tmp-name1))))
4009	    ;; Skip the test, if the remote handler is not able to set
4010	    ;; the correct time.
4011	    (skip-unless (set-file-times tmp-name1 (seconds-to-time 1)))
4012	    ;; Dumb remote shells without perl(1) or stat(1) are not
4013	    ;; able to return the date correctly.  They say "don't know".
4014	    (unless (tramp-compat-time-equal-p
4015		     (file-attribute-modification-time
4016		      (file-attributes tmp-name1))
4017		     tramp-time-dont-know)
4018	      (should
4019	       (tramp-compat-time-equal-p
4020                (file-attribute-modification-time (file-attributes tmp-name1))
4021		(seconds-to-time 1)))
4022	      (write-region "bla" nil tmp-name2)
4023	      (should (file-exists-p tmp-name2))
4024	      (should (file-newer-than-file-p tmp-name2 tmp-name1))
4025	      ;; `tmp-name3' does not exist.
4026	      (should (file-newer-than-file-p tmp-name2 tmp-name3))
4027	      (should-not (file-newer-than-file-p tmp-name3 tmp-name1))
4028	      ;; Check the NOFOLLOW arg.  It exists since Emacs 28.  For
4029	      ;; regular files, there shouldn't be a difference.
4030	      (when (tramp--test-emacs28-p)
4031		(with-no-warnings
4032		  (set-file-times tmp-name1 (seconds-to-time 1) 'nofollow)
4033		  (should
4034		   (tramp-compat-time-equal-p
4035                    (file-attribute-modification-time
4036		     (file-attributes tmp-name1))
4037		    (seconds-to-time 1)))))))
4038
4039	;; Cleanup.
4040	(ignore-errors
4041	  (delete-file tmp-name1)
4042	  (delete-file tmp-name2))))))
4043
4044(ert-deftest tramp-test23-visited-file-modtime ()
4045  "Check `set-visited-file-modtime' and `verify-visited-file-modtime'."
4046  (skip-unless (tramp--test-enabled))
4047
4048  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
4049    (let ((tmp-name (tramp--test-make-temp-name nil quoted)))
4050      (unwind-protect
4051	  (progn
4052	    (write-region "foo" nil tmp-name)
4053	    (should (file-exists-p tmp-name))
4054	    (with-temp-buffer
4055	      (insert-file-contents tmp-name)
4056	      (should (verify-visited-file-modtime))
4057              (set-visited-file-modtime (seconds-to-time 1))
4058	      (should (verify-visited-file-modtime))
4059	      (should (= 1 (float-time (visited-file-modtime))))
4060
4061	      ;; Checks with deleted file.
4062	      (delete-file tmp-name)
4063	      (dired-uncache tmp-name)
4064	      (should (verify-visited-file-modtime))
4065              (set-visited-file-modtime (seconds-to-time 1))
4066	      (should (verify-visited-file-modtime))
4067	      (should (= 1 (float-time (visited-file-modtime))))))
4068
4069	;; Cleanup.
4070	(ignore-errors (delete-file tmp-name))))))
4071
4072;; This test is inspired by Bug#29149.
4073(ert-deftest tramp-test24-file-acl ()
4074  "Check that `file-acl' and `set-file-acl' work proper."
4075  (skip-unless (tramp--test-enabled))
4076  ;; The following test checks also whether `set-file-modes' will work.
4077  (skip-unless (file-acl tramp-test-temporary-file-directory))
4078  (skip-unless (not (tramp--test-crypt-p)))
4079
4080  ;; `filename-non-special' has been fixed in Emacs 27.1, see Bug#29579.
4081  (dolist (quoted
4082	   (if (and (tramp--test-expensive-test-p) (tramp--test-emacs27-p))
4083	       '(nil t) '(nil)))
4084    (let ((tmp-name1 (tramp--test-make-temp-name nil quoted))
4085	  (tmp-name2 (tramp--test-make-temp-name nil quoted))
4086	  (tmp-name3 (tramp--test-make-temp-name 'local quoted)))
4087      ;; Both files are remote.
4088      (unwind-protect
4089	  (progn
4090	    ;; Two files with same ACLs.
4091	    (write-region "foo" nil tmp-name1)
4092	    (should (file-exists-p tmp-name1))
4093	    (should (file-acl tmp-name1))
4094	    (copy-file tmp-name1 tmp-name2 nil nil nil 'preserve-permissions)
4095	    (should (file-acl tmp-name2))
4096	    (should (string-equal (file-acl tmp-name1) (file-acl tmp-name2)))
4097	    ;; Different permissions mean different ACLs.
4098	    (unless (tramp--test-windows-nt-or-smb-p)
4099	      (set-file-modes tmp-name1 #o777)
4100	      (set-file-modes tmp-name2 #o444)
4101	      (should-not
4102	       (string-equal (file-acl tmp-name1) (file-acl tmp-name2))))
4103	    ;; Copy ACL.  Not all remote handlers support it, so we test.
4104	    (when (set-file-acl tmp-name2 (file-acl tmp-name1))
4105	      (should (string-equal (file-acl tmp-name1) (file-acl tmp-name2))))
4106	    ;; An invalid ACL does not harm.
4107	    (should-not (set-file-acl tmp-name2 "foo")))
4108
4109	;; Cleanup.
4110	(ignore-errors (delete-file tmp-name1))
4111	(ignore-errors (delete-file tmp-name2)))
4112
4113      ;; Remote and local file.
4114      (unwind-protect
4115	  (when (and (file-acl temporary-file-directory)
4116		     (not (tramp--test-windows-nt-or-smb-p)))
4117	    ;; Two files with same ACLs.
4118	    (write-region "foo" nil tmp-name1)
4119	    (should (file-exists-p tmp-name1))
4120	    (should (file-acl tmp-name1))
4121	    (copy-file tmp-name1 tmp-name3 nil nil nil 'preserve-permissions)
4122	    (should (file-acl tmp-name3))
4123	    (should (string-equal (file-acl tmp-name1) (file-acl tmp-name3)))
4124	    ;; Different permissions mean different ACLs.
4125	    (set-file-modes tmp-name1 #o777)
4126	    (set-file-modes tmp-name3 #o444)
4127	    (should-not
4128	     (string-equal (file-acl tmp-name1) (file-acl tmp-name3)))
4129	    ;; Copy ACL.  Since we don't know whether Emacs is built
4130	    ;; with local ACL support, we must check it.
4131	    (when (set-file-acl tmp-name3 (file-acl tmp-name1))
4132	      (should (string-equal (file-acl tmp-name1) (file-acl tmp-name3))))
4133
4134	    ;; Two files with same ACLs.
4135	    (delete-file tmp-name1)
4136	    (copy-file tmp-name3 tmp-name1 nil nil nil 'preserve-permissions)
4137	    (should (file-acl tmp-name1))
4138	    (should (string-equal (file-acl tmp-name1) (file-acl tmp-name3)))
4139	    ;; Different permissions mean different ACLs.
4140	    (set-file-modes tmp-name1 #o777)
4141	    (set-file-modes tmp-name3 #o444)
4142	    (should-not
4143	     (string-equal (file-acl tmp-name1) (file-acl tmp-name3)))
4144	    ;; Copy ACL.
4145	    (set-file-acl tmp-name1 (file-acl tmp-name3))
4146	    (should (string-equal (file-acl tmp-name1) (file-acl tmp-name3))))
4147
4148	;; Cleanup.
4149	(ignore-errors (delete-file tmp-name1))
4150	(ignore-errors (delete-file tmp-name3))))))
4151
4152(ert-deftest tramp-test25-file-selinux ()
4153  "Check `file-selinux-context' and `set-file-selinux-context'."
4154  (skip-unless (tramp--test-enabled))
4155  (skip-unless
4156   (not (equal (file-selinux-context tramp-test-temporary-file-directory)
4157	       '(nil nil nil nil))))
4158  (skip-unless (not (tramp--test-crypt-p)))
4159
4160  ;; `filename-non-special' has been fixed in Emacs 27.1, see Bug#29579.
4161  (dolist (quoted
4162	   (if (and (tramp--test-expensive-test-p) (tramp--test-emacs27-p))
4163	       '(nil t) '(nil)))
4164    (let ((tmp-name1 (tramp--test-make-temp-name nil quoted))
4165	  (tmp-name2 (tramp--test-make-temp-name nil quoted))
4166	  (tmp-name3 (tramp--test-make-temp-name 'local quoted)))
4167      ;; Both files are remote.
4168      (unwind-protect
4169	  (progn
4170	    ;; Two files with same SELinux context.
4171	    (write-region "foo" nil tmp-name1)
4172	    (should (file-exists-p tmp-name1))
4173	    (should (file-selinux-context tmp-name1))
4174	    (copy-file tmp-name1 tmp-name2 nil nil nil 'preserve-permissions)
4175	    (should (file-selinux-context tmp-name2))
4176	    (should
4177	     (equal
4178	      (file-selinux-context tmp-name1)
4179	      (file-selinux-context tmp-name2)))
4180	    ;; Check different SELinux context.  We cannot support
4181	    ;; different ranges in this test; let's assume the most
4182	    ;; likely one.
4183	    (let ((context (file-selinux-context tmp-name1)))
4184	      (when (and (string-equal (nth 3 context) "s0")
4185			 (setcar (nthcdr 3 context) "s0:c0")
4186			 (set-file-selinux-context tmp-name1 context))
4187		(should-not
4188		 (equal
4189		  (file-selinux-context tmp-name1)
4190		  (file-selinux-context tmp-name2)))))
4191	    ;; Copy SELinux context.
4192	    (should
4193	     (set-file-selinux-context
4194	      tmp-name2 (file-selinux-context tmp-name1)))
4195	    (should
4196	     (equal
4197	      (file-selinux-context tmp-name1)
4198	      (file-selinux-context tmp-name2)))
4199	    ;; An invalid SELinux context does not harm.
4200	    (should-not (set-file-selinux-context tmp-name2 "foo")))
4201
4202	;; Cleanup.
4203	(ignore-errors (delete-file tmp-name1))
4204	(ignore-errors (delete-file tmp-name2)))
4205
4206      ;; Remote and local file.
4207      (unwind-protect
4208	  (when (and (not
4209		      (or (equal (file-selinux-context temporary-file-directory)
4210				 '(nil nil nil nil))
4211			  (tramp--test-windows-nt-or-smb-p)))
4212		     ;; Both users shall use the same SELinux context.
4213		     (string-equal
4214		      (let ((default-directory temporary-file-directory))
4215			(shell-command-to-string "id -Z"))
4216		      (let ((default-directory
4217			      tramp-test-temporary-file-directory))
4218			(shell-command-to-string "id -Z"))))
4219
4220	    ;; Two files with same SELinux context.
4221	    (write-region "foo" nil tmp-name1)
4222	    (should (file-exists-p tmp-name1))
4223	    (should (file-selinux-context tmp-name1))
4224	    (copy-file tmp-name1 tmp-name3)
4225	    (should (file-selinux-context tmp-name3))
4226	    ;; We cannot expect that copying over file system
4227	    ;; boundaries keeps SELinux context.  So we copy it
4228	    ;; explicitly.
4229	    (should
4230	     (set-file-selinux-context
4231	      tmp-name3 (file-selinux-context tmp-name1)))
4232	    (should
4233	     (equal
4234	      (file-selinux-context tmp-name1)
4235	      (file-selinux-context tmp-name3)))
4236	    ;; Check different SELinux context.  We cannot support
4237	    ;; different ranges in this test; let's assume the most
4238	    ;; likely one.
4239	    (let ((context (file-selinux-context tmp-name1)))
4240	      (when (and (string-equal (nth 3 context) "s0")
4241			 (setcar (nthcdr 3 context) "s0:c0")
4242			 (set-file-selinux-context tmp-name1 context))
4243		(should-not
4244		 (equal
4245		  (file-selinux-context tmp-name1)
4246		  (file-selinux-context tmp-name3)))))
4247	    ;; Copy SELinux context.
4248	    (should
4249	     (set-file-selinux-context
4250	      tmp-name3 (file-selinux-context tmp-name1)))
4251	    (should
4252	     (equal
4253	      (file-selinux-context tmp-name1)
4254	      (file-selinux-context tmp-name3)))
4255
4256	    ;; Two files with same SELinux context.
4257	    (delete-file tmp-name1)
4258	    (copy-file tmp-name3 tmp-name1)
4259	    (should (file-selinux-context tmp-name1))
4260	    ;; We cannot expect that copying over file system
4261	    ;; boundaries keeps SELinux context.  So we copy it
4262	    ;; explicitly.
4263	    (should
4264	     (set-file-selinux-context
4265	      tmp-name1 (file-selinux-context tmp-name3)))
4266	    (should
4267	     (equal
4268	      (file-selinux-context tmp-name1)
4269	      (file-selinux-context tmp-name3)))
4270	    ;; Check different SELinux context.  We cannot support
4271	    ;; different ranges in this test; let's assume the most
4272	    ;; likely one.
4273	    (let ((context (file-selinux-context tmp-name3)))
4274	      (when (and (string-equal (nth 3 context) "s0")
4275			 (setcar (nthcdr 3 context) "s0:c0")
4276			 (set-file-selinux-context tmp-name3 context))
4277		(should-not
4278		 (equal
4279		  (file-selinux-context tmp-name1)
4280		  (file-selinux-context tmp-name3)))))
4281	    ;; Copy SELinux context.
4282	    (should
4283	     (set-file-selinux-context
4284	      tmp-name1 (file-selinux-context tmp-name3)))
4285	    (should
4286	     (equal
4287	      (file-selinux-context tmp-name1)
4288	      (file-selinux-context tmp-name3))))
4289
4290	;; Cleanup.
4291	(ignore-errors (delete-file tmp-name1))
4292	(ignore-errors (delete-file tmp-name3))))))
4293
4294(ert-deftest tramp-test26-file-name-completion ()
4295  "Check `file-name-completion' and `file-name-all-completions'."
4296  (skip-unless (tramp--test-enabled))
4297
4298  ;; Method and host name in completion mode.  This kind of completion
4299  ;; does not work on MS Windows.
4300  (unless (memq system-type '(cygwin windows-nt))
4301    (let ((method (file-remote-p tramp-test-temporary-file-directory 'method))
4302	  (host (file-remote-p tramp-test-temporary-file-directory 'host))
4303          (orig-syntax tramp-syntax))
4304      (when (and (stringp host) (string-match tramp-host-with-port-regexp host))
4305	(setq host (match-string 1 host)))
4306
4307      (unwind-protect
4308          (dolist
4309	      (syntax
4310	       (if (tramp--test-expensive-test-p)
4311		   (tramp-syntax-values) `(,orig-syntax)))
4312            (tramp-change-syntax syntax)
4313	    ;; This has cleaned up all connection data, which are used
4314	    ;; for completion.  We must refill the cache.
4315	    (tramp-set-connection-property tramp-test-vec "property" nil)
4316
4317            (let ;; This is needed for the `separate' syntax.
4318                ((prefix-format (substring tramp-prefix-format 1))
4319		 ;; This is needed for the IPv6 host name syntax.
4320		 (ipv6-prefix
4321		  (and (string-match-p tramp-ipv6-regexp host)
4322		       tramp-prefix-ipv6-format))
4323		 (ipv6-postfix
4324		  (and (string-match-p tramp-ipv6-regexp host)
4325		       tramp-postfix-ipv6-format)))
4326              ;; Complete method name.
4327	      (unless (or (zerop (length method))
4328                          (zerop (length tramp-method-regexp)))
4329	        (should
4330	         (member
4331		  (concat prefix-format method tramp-postfix-method-format)
4332		  (file-name-all-completions
4333                   (concat prefix-format (substring method 0 1)) "/"))))
4334              ;; Complete host name.
4335	      (unless (or (zerop (length method))
4336                          (zerop (length tramp-method-regexp))
4337                          (zerop (length host))
4338			  (tramp--test-gvfs-p method))
4339	        (should
4340	         (member
4341		  (concat
4342                   prefix-format method tramp-postfix-method-format
4343		   ipv6-prefix host ipv6-postfix tramp-postfix-host-format)
4344		  (file-name-all-completions
4345		   (concat prefix-format method tramp-postfix-method-format)
4346                   "/"))))))
4347
4348	;; Cleanup.
4349        (tramp-change-syntax orig-syntax))))
4350
4351  (dolist (non-essential '(nil t))
4352    (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
4353      (let ((tmp-name (tramp--test-make-temp-name nil quoted)))
4354
4355	(unwind-protect
4356	    (progn
4357	      ;; Local files.
4358	      (make-directory tmp-name)
4359	      (should (file-directory-p tmp-name))
4360	      (write-region "foo" nil (expand-file-name "foo" tmp-name))
4361	      (should (file-exists-p (expand-file-name "foo" tmp-name)))
4362	      (write-region "bar" nil (expand-file-name "bold" tmp-name))
4363	      (should (file-exists-p (expand-file-name "bold" tmp-name)))
4364	      (make-directory (expand-file-name "boz" tmp-name))
4365	      (should (file-directory-p (expand-file-name "boz" tmp-name)))
4366	      (should (equal (file-name-completion "fo" tmp-name) "foo"))
4367	      (should (equal (file-name-completion "foo" tmp-name) t))
4368	      (should (equal (file-name-completion "b" tmp-name) "bo"))
4369	      (should-not (file-name-completion "a" tmp-name))
4370	      ;; Ange-FTP does not support predicates.
4371	      (unless (tramp--test-ange-ftp-p)
4372		(should
4373		 (equal
4374		  (file-name-completion "b" tmp-name #'file-directory-p)
4375		  "boz/")))
4376	      (should
4377	       (equal (file-name-all-completions "fo" tmp-name) '("foo")))
4378	      (should
4379	       (equal
4380		(sort (file-name-all-completions "b" tmp-name) #'string-lessp)
4381		'("bold" "boz/")))
4382	      (should-not (file-name-all-completions "a" tmp-name))
4383	      ;; `completion-regexp-list' restricts the completion to
4384	      ;; files which match all expressions in this list.
4385	      ;; Ange-FTP does not complete "".
4386	      (unless (tramp--test-ange-ftp-p)
4387		(let ((completion-regexp-list
4388		       `(,directory-files-no-dot-files-regexp "b")))
4389		  (should
4390		   (equal (file-name-completion "" tmp-name) "bo"))
4391		  (should
4392		   (equal
4393		    (sort
4394		     (file-name-all-completions "" tmp-name) #'string-lessp)
4395		    '("bold" "boz/")))))
4396	      ;; `file-name-completion' ignores file names that end in
4397	      ;; any string in `completion-ignored-extensions'.
4398	      (let ((completion-ignored-extensions '(".ext")))
4399		(write-region "foo" nil (expand-file-name "foo.ext" tmp-name))
4400		(should (file-exists-p (expand-file-name "foo.ext" tmp-name)))
4401		(should (equal (file-name-completion "fo" tmp-name) "foo"))
4402		(should (equal (file-name-completion "foo" tmp-name) t))
4403		(should
4404		 (equal (file-name-completion "foo." tmp-name) "foo.ext"))
4405		(should (equal (file-name-completion "foo.ext" tmp-name) t))
4406		;; `file-name-all-completions' is not affected.
4407		(should
4408		 (equal
4409		  (sort (file-name-all-completions "" tmp-name) #'string-lessp)
4410		  '("../" "./" "bold" "boz/" "foo" "foo.ext")))))
4411
4412	  ;; Cleanup.
4413	  (ignore-errors (delete-directory tmp-name 'recursive)))))))
4414
4415(ert-deftest tramp-test27-load ()
4416  "Check `load'."
4417  (skip-unless (tramp--test-enabled))
4418
4419  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
4420    (let ((tmp-name (tramp--test-make-temp-name nil quoted)))
4421      (unwind-protect
4422	  (progn
4423	    (load tmp-name 'noerror 'nomessage)
4424	    (should-not (featurep 'tramp-test-load))
4425	    (write-region "(provide 'tramp-test-load)" nil tmp-name)
4426	    ;; `load' in lread.c does not pass `must-suffix'.  Why?
4427	    ;;(should-error
4428	    ;; (load tmp-name nil 'nomessage 'nosuffix 'must-suffix)
4429	    ;; :type 'file-error)
4430	    (load tmp-name nil 'nomessage 'nosuffix)
4431	    (should (featurep 'tramp-test-load)))
4432
4433	;; Cleanup.
4434	(ignore-errors
4435	  (and (featurep 'tramp-test-load) (unload-feature 'tramp-test-load))
4436	  (delete-file tmp-name))))))
4437
4438(defun tramp--test-shell-file-name ()
4439  "Return default remote shell."
4440  (if (tramp--test-adb-p) "/system/bin/sh" "/bin/sh"))
4441
4442(ert-deftest tramp-test28-process-file ()
4443  "Check `process-file'."
4444  :tags '(:expensive-test)
4445  (skip-unless (tramp--test-enabled))
4446  (skip-unless (tramp--test-supports-processes-p))
4447
4448  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
4449    (let* ((tmp-name (tramp--test-make-temp-name nil quoted))
4450	   (fnnd (file-name-nondirectory tmp-name))
4451	   (default-directory tramp-test-temporary-file-directory)
4452	   kill-buffer-query-functions)
4453      (unwind-protect
4454	  (progn
4455	    ;; We cannot use "/bin/true" and "/bin/false"; those paths
4456	    ;; do not exist on hydra.
4457	    (should (zerop (process-file "true")))
4458	    (should-not (zerop (process-file "false")))
4459	    (should-not (zerop (process-file "binary-does-not-exist")))
4460	    ;; Return exit code.
4461	    (should (= 42 (process-file
4462			   (tramp--test-shell-file-name)
4463			   nil nil nil "-c" "exit 42")))
4464	    ;; Return exit code in case the process is interrupted,
4465	    ;; and there's no indication for a signal describing string.
4466	    (unless (tramp--test-sshfs-p)
4467	      (let (process-file-return-signal-string)
4468		(should
4469		 (= (+ 128 2)
4470		    (process-file
4471		     (tramp--test-shell-file-name)
4472		     nil nil nil "-c" "kill -2 $$")))))
4473	    ;; Return string in case the process is interrupted and
4474	    ;; there's an indication for a signal describing string.
4475	    (unless (tramp--test-sshfs-p)
4476	      (let ((process-file-return-signal-string t))
4477		(should
4478		 (string-match-p
4479		  "Interrupt\\|Signal 2"
4480		  (process-file
4481		   (tramp--test-shell-file-name)
4482		   nil nil nil "-c" "kill -2 $$")))))
4483
4484	    (with-temp-buffer
4485	      (write-region "foo" nil tmp-name)
4486	      (should (file-exists-p tmp-name))
4487	      (should (zerop (process-file "ls" nil t nil fnnd)))
4488	      ;; "ls" could produce colorized output.
4489	      (goto-char (point-min))
4490	      (while
4491		  (re-search-forward tramp-display-escape-sequence-regexp nil t)
4492		(replace-match "" nil nil))
4493	      (should (string-equal (format "%s\n" fnnd) (buffer-string)))
4494	      (should-not (get-buffer-window (current-buffer) t))
4495
4496	      ;; Second run.  The output must be appended.
4497	      (goto-char (point-max))
4498	      (should (zerop (process-file "ls" nil t t fnnd)))
4499	      ;; "ls" could produce colorized output.
4500	      (goto-char (point-min))
4501	      (while
4502		  (re-search-forward tramp-display-escape-sequence-regexp nil t)
4503		(replace-match "" nil nil))
4504	      (should
4505	       (string-equal (format "%s\n%s\n" fnnd fnnd) (buffer-string)))
4506	      ;; A non-nil DISPLAY must not raise the buffer.
4507	      (should-not (get-buffer-window (current-buffer) t))))
4508
4509	;; Cleanup.
4510	(ignore-errors (delete-file tmp-name))))))
4511
4512;; Must be a command, because used as `sigusr1' handler.
4513(defun tramp--test-timeout-handler (&rest _ignore)
4514  "Timeout handler, reporting a failed test."
4515  (interactive)
4516  (let ((proc (get-buffer-process (current-buffer))))
4517    (when (processp proc)
4518      (tramp--test-message
4519       "cmd: %s\nbuf:\n%s\n---" (process-command proc) (buffer-string))))
4520  (ert-fail (format "`%s' timed out" (ert-test-name (ert-running-test)))))
4521
4522(ert-deftest tramp-test29-start-file-process ()
4523  "Check `start-file-process'."
4524  :tags '(:expensive-test :tramp-asynchronous-processes)
4525  (skip-unless (tramp--test-enabled))
4526  (skip-unless (tramp--test-supports-processes-p))
4527
4528  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
4529    (let ((default-directory tramp-test-temporary-file-directory)
4530	  (tmp-name (tramp--test-make-temp-name nil quoted))
4531	  kill-buffer-query-functions proc)
4532
4533      ;; Simple process.
4534      (unwind-protect
4535	  (with-temp-buffer
4536	    (setq proc (start-file-process "test1" (current-buffer) "cat"))
4537	    (should (processp proc))
4538	    (should (equal (process-status proc) 'run))
4539	    (process-send-string proc "foo\n")
4540	    (process-send-eof proc)
4541	    ;; Read output.
4542	    (with-timeout (10 (tramp--test-timeout-handler))
4543	      (while (< (- (point-max) (point-min)) (length "foo"))
4544		(while (accept-process-output proc 0 nil t))))
4545	    (should (string-match-p "foo" (buffer-string))))
4546
4547	;; Cleanup.
4548	(ignore-errors (delete-process proc)))
4549
4550      ;; Simple process using a file.
4551      (unwind-protect
4552	  (with-temp-buffer
4553	    (write-region "foo" nil tmp-name)
4554	    (should (file-exists-p tmp-name))
4555	    (setq proc
4556		  (start-file-process
4557		   "test2" (current-buffer)
4558		   "cat" (file-name-nondirectory tmp-name)))
4559	    (should (processp proc))
4560	    ;; Read output.
4561	    (with-timeout (10 (tramp--test-timeout-handler))
4562	      (while (< (- (point-max) (point-min)) (length "foo"))
4563		(while (accept-process-output proc 0 nil t))))
4564	    (should (string-match-p "foo" (buffer-string))))
4565
4566	;; Cleanup.
4567	(ignore-errors
4568	  (delete-process proc)
4569	  (delete-file tmp-name)))
4570
4571      ;; Process filter.
4572      (unwind-protect
4573	  (with-temp-buffer
4574	    (setq proc (start-file-process "test3" (current-buffer) "cat"))
4575	    (should (processp proc))
4576	    (should (equal (process-status proc) 'run))
4577	    (set-process-filter
4578	     proc
4579	     (lambda (p s) (with-current-buffer (process-buffer p) (insert s))))
4580	    (process-send-string proc "foo\n")
4581	    (process-send-eof proc)
4582	    ;; Read output.
4583	    (with-timeout (10 (tramp--test-timeout-handler))
4584	      (while (< (- (point-max) (point-min)) (length "foo"))
4585		(while (accept-process-output proc 0 nil t))))
4586	    (should (string-match-p "foo" (buffer-string))))
4587
4588	;; Cleanup.
4589	(ignore-errors (delete-process proc)))
4590
4591      ;; Disabled process filter.  "sshfs" does not cooperate.
4592      (unless (tramp--test-sshfs-p)
4593	(unwind-protect
4594	    (with-temp-buffer
4595	      (setq proc (start-file-process "test3" (current-buffer) "cat"))
4596	      (should (processp proc))
4597	      (should (equal (process-status proc) 'run))
4598	      (set-process-filter proc t)
4599	      (process-send-string proc "foo\n")
4600	      (process-send-eof proc)
4601	      ;; Read output.  There shouldn't be any.
4602	      (with-timeout (10)
4603		(while (process-live-p proc)
4604		  (while (accept-process-output proc 0 nil t))))
4605	      ;; No output due to process filter.
4606	      (should (= (point-min) (point-max))))
4607
4608	  ;; Cleanup.
4609	  (ignore-errors (delete-process proc))))
4610
4611      ;; Process connection type.
4612      (when (and (tramp--test-sh-p)
4613		 (not (tramp-direct-async-process-p))
4614		 ;; `executable-find' has changed the number of
4615		 ;; parameters in Emacs 27.1, so we use `apply' for
4616		 ;; older Emacsen.
4617		 (ignore-errors
4618		   (with-no-warnings
4619		     (apply #'executable-find '("hexdump" remote)))))
4620	(dolist (process-connection-type '(nil pipe t pty))
4621	  (unwind-protect
4622	      (with-temp-buffer
4623		(setq proc
4624		      (start-file-process
4625		       (format "test4-%s" process-connection-type)
4626		       (current-buffer) "hexdump" "-v" "-e" "/1 \"%02X\n\""))
4627		(should (processp proc))
4628		(should (equal (process-status proc) 'run))
4629		(process-send-string proc "foo\r\n")
4630		(process-send-eof proc)
4631		;; Read output.
4632		(with-timeout (10 (tramp--test-timeout-handler))
4633		  (while (< (- (point-max) (point-min))
4634			    (length "66\n6F\n6F\n0D\n0A\n"))
4635		    (while (accept-process-output proc 0 nil t))))
4636		(should
4637		 (string-match-p
4638		  (if (and (memq process-connection-type '(nil pipe))
4639                           (not (tramp--test-macos-p)))
4640                      ;; On macOS, there is always newline conversion.
4641		      ;; "telnet" converts \r to <CR><NUL> if `crlf'
4642		      ;; flag is FALSE.  See telnet(1) man page.
4643		      "66\n6F\n6F\n0D\\(\n00\\)?\n0A\n"
4644		    "66\n6F\n6F\n0A\\(\n00\\)?\n0A\n")
4645		  (buffer-string))))
4646
4647	    ;; Cleanup.
4648	    (ignore-errors (delete-process proc)))))
4649
4650      ;; PTY.
4651      (unwind-protect
4652	  (with-temp-buffer
4653	    ;; It works only for tramp-sh.el, and not direct async processes.
4654	    (if (or (not (tramp--test-sh-p)) (tramp-direct-async-process-p))
4655		(should-error
4656		 (start-file-process "test5" (current-buffer) nil)
4657		 :type 'wrong-type-argument)
4658
4659	      (setq proc (start-file-process "test5" (current-buffer) nil))
4660	      (should (processp proc))
4661	      (should (equal (process-status proc) 'run))
4662	      ;; On MS Windows, `process-tty-name' returns nil.
4663	      (unless (tramp--test-windows-nt-p)
4664		(should (stringp (process-tty-name proc))))))
4665
4666	;; Cleanup.
4667	(ignore-errors (delete-process proc))))))
4668
4669(defmacro tramp--test--deftest-direct-async-process
4670    (test docstring &optional unstable)
4671  "Define ert test `TEST-direct-async' for direct async processes.
4672If UNSTABLE is non-nil, the test is tagged as `:unstable'."
4673  (declare (indent 1))
4674  ;; `make-process' supports file name handlers since Emacs 27.  We
4675  ;; cannot use `tramp--test-always' during compilation of the macro.
4676  (when (let ((file-name-handler-alist '(("" . (lambda (&rest _) t)))))
4677	  (ignore-errors (make-process :file-handler t)))
4678    `(ert-deftest ,(intern (concat (symbol-name test) "-direct-async")) ()
4679       ,docstring
4680       :tags (append '(:expensive-test :tramp-asynchronous-processes)
4681		     (and ,unstable '(:unstable)))
4682       (skip-unless (tramp--test-enabled))
4683       (let ((default-directory tramp-test-temporary-file-directory)
4684	     (ert-test (ert-get-test ',test))
4685	     (tramp-connection-properties
4686	      (cons '(nil "direct-async-process" t)
4687		    tramp-connection-properties)))
4688	 (skip-unless (tramp-direct-async-process-p))
4689	 ;; We do expect an established connection already,
4690	 ;; `file-truename' does it by side-effect.  Suppress
4691	 ;; `tramp--test-enabled', in order to keep the connection.
4692	 ;; Suppress "Process ... finished" messages.
4693	 (cl-letf (((symbol-function #'tramp--test-enabled) #'tramp--test-always)
4694		   ((symbol-function #'internal-default-process-sentinel)
4695		    #'ignore))
4696	   (file-truename tramp-test-temporary-file-directory)
4697	   (funcall (ert-test-body ert-test)))))))
4698
4699(tramp--test--deftest-direct-async-process tramp-test29-start-file-process
4700  "Check direct async `start-file-process'.")
4701
4702(ert-deftest tramp-test30-make-process ()
4703  "Check `make-process'."
4704  :tags '(:expensive-test :tramp-asynchronous-processes)
4705  (skip-unless (tramp--test-enabled))
4706  (skip-unless (tramp--test-supports-processes-p))
4707  ;; `make-process' supports file name handlers since Emacs 27.
4708  (skip-unless (tramp--test-emacs27-p))
4709
4710  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
4711    (let ((default-directory tramp-test-temporary-file-directory)
4712	  (tmp-name (tramp--test-make-temp-name nil quoted))
4713	  kill-buffer-query-functions proc)
4714      (with-no-warnings (should-not (make-process)))
4715
4716      ;; Simple process.
4717      (unwind-protect
4718	  (with-temp-buffer
4719	    (setq proc
4720		  (with-no-warnings
4721		    (make-process
4722		     :name "test1" :buffer (current-buffer) :command '("cat")
4723		     :file-handler t)))
4724	    (should (processp proc))
4725	    (should (equal (process-status proc) 'run))
4726	    (process-send-string proc "foo\n")
4727	    (process-send-eof proc)
4728	    ;; Read output.
4729	    (with-timeout (10 (tramp--test-timeout-handler))
4730	      (while (< (- (point-max) (point-min)) (length "foo"))
4731		(while (accept-process-output proc 0 nil t))))
4732	    (should (string-match-p "foo" (buffer-string))))
4733
4734	;; Cleanup.
4735	(ignore-errors (delete-process proc)))
4736
4737      ;; Simple process using a file.
4738      (unwind-protect
4739	  (with-temp-buffer
4740	    (write-region "foo" nil tmp-name)
4741	    (should (file-exists-p tmp-name))
4742	    (setq proc
4743		  (with-no-warnings
4744		    (make-process
4745		     :name "test2" :buffer (current-buffer)
4746		     :command `("cat" ,(file-name-nondirectory tmp-name))
4747		     :file-handler t)))
4748	    (should (processp proc))
4749	    ;; Read output.
4750	    (with-timeout (10 (tramp--test-timeout-handler))
4751	      (while (< (- (point-max) (point-min)) (length "foo"))
4752		(while (accept-process-output proc 0 nil t))))
4753	    (should (string-match-p "foo" (buffer-string))))
4754
4755	;; Cleanup.
4756	(ignore-errors
4757	  (delete-process proc)
4758	  (delete-file tmp-name)))
4759
4760      ;; Process filter.
4761      (unwind-protect
4762	  (with-temp-buffer
4763	    (setq proc
4764		  (with-no-warnings
4765		    (make-process
4766		     :name "test3" :buffer (current-buffer) :command '("cat")
4767		     :filter
4768		     (lambda (p s)
4769		       (with-current-buffer (process-buffer p) (insert s)))
4770		     :file-handler t)))
4771	    (should (processp proc))
4772	    (should (equal (process-status proc) 'run))
4773	    (process-send-string proc "foo\n")
4774	    (process-send-eof proc)
4775	    ;; Read output.
4776	    (with-timeout (10 (tramp--test-timeout-handler))
4777	      (while (not (string-match-p "foo" (buffer-string)))
4778		(while (accept-process-output proc 0 nil t))))
4779	    (should (string-match-p "foo" (buffer-string))))
4780
4781	;; Cleanup.
4782	(ignore-errors (delete-process proc)))
4783
4784      ;; Disabled process filter.  "sshfs" does not cooperate.
4785      (unless (tramp--test-sshfs-p)
4786	(unwind-protect
4787	    (with-temp-buffer
4788	      (setq proc
4789		    (with-no-warnings
4790		      (make-process
4791		       :name "test3" :buffer (current-buffer) :command '("cat")
4792		       :filter t
4793		       :file-handler t)))
4794	      (should (processp proc))
4795	      (should (equal (process-status proc) 'run))
4796	      (process-send-string proc "foo\n")
4797	      (process-send-eof proc)
4798	      ;; Read output.  There shouldn't be any.
4799	      (with-timeout (10)
4800		(while (process-live-p proc)
4801		  (while (accept-process-output proc 0 nil t))))
4802	      ;; No output due to process filter.
4803	      (should (= (point-min) (point-max))))
4804
4805	  ;; Cleanup.
4806	  (ignore-errors (delete-process proc))))
4807
4808      ;; Process sentinel.
4809      (unwind-protect
4810	  (with-temp-buffer
4811	    (setq proc
4812		  (with-no-warnings
4813		    (make-process
4814		     :name "test4" :buffer (current-buffer) :command '("cat")
4815		     :sentinel
4816		     (lambda (p s)
4817		       (with-current-buffer (process-buffer p) (insert s)))
4818		     :file-handler t)))
4819	    (should (processp proc))
4820	    (should (equal (process-status proc) 'run))
4821	    (process-send-string proc "foo\n")
4822	    (process-send-eof proc)
4823	    (delete-process proc)
4824	    ;; Read output.
4825	    (with-timeout (10 (tramp--test-timeout-handler))
4826	      (while (accept-process-output proc 0 nil t)))
4827	    ;; On some MS Windows systems, it returns "unknown signal".
4828	    (should (string-match-p "unknown signal\\|killed" (buffer-string))))
4829
4830	;; Cleanup.
4831	(ignore-errors (delete-process proc)))
4832
4833      ;; Process with stderr buffer.  "telnet" does not cooperate with
4834      ;; three processes.
4835      (unless (or (tramp--test-telnet-p) (tramp-direct-async-process-p))
4836	(let ((stderr (generate-new-buffer "*stderr*")))
4837	  (unwind-protect
4838	      (with-temp-buffer
4839		(setq proc
4840		      (with-no-warnings
4841			(make-process
4842			 :name "test5" :buffer (current-buffer)
4843			 :command '("cat" "/does-not-exist")
4844			 :stderr stderr
4845			 :file-handler t)))
4846		(should (processp proc))
4847		;; Read output.
4848		(with-timeout (10 (tramp--test-timeout-handler))
4849		  (while (accept-process-output proc 0 nil t)))
4850		;; Read stderr.
4851		(with-current-buffer stderr
4852		  (with-timeout (10 (tramp--test-timeout-handler))
4853		    (while (not (string-match-p
4854				 "No such file or directory" (buffer-string)))
4855		      (while (accept-process-output
4856			      (get-buffer-process stderr) 0 nil t))))
4857		  (delete-process proc)
4858		  (should
4859		   (string-match-p
4860		    "cat:.* No such file or directory" (buffer-string)))))
4861
4862	    ;; Cleanup.
4863	    (ignore-errors (delete-process proc))
4864	    (ignore-errors (kill-buffer stderr)))))
4865
4866      ;; Process with stderr file.
4867      (unless (tramp-direct-async-process-p)
4868	(unwind-protect
4869	    (with-temp-buffer
4870	      (setq proc
4871		    (with-no-warnings
4872		      (make-process
4873		       :name "test6" :buffer (current-buffer)
4874		       :command '("cat" "/does-not-exist")
4875		       :stderr tmp-name
4876		       :file-handler t)))
4877	      (should (processp proc))
4878	      ;; Read stderr.
4879	      (with-timeout (10 (tramp--test-timeout-handler))
4880		(while (accept-process-output proc nil nil t)))
4881	      (delete-process proc)
4882	      (with-temp-buffer
4883		(insert-file-contents tmp-name)
4884		(should
4885		 (string-match-p
4886		  "cat:.* No such file or directory" (buffer-string)))))
4887
4888	  ;; Cleanup.
4889	  (ignore-errors (delete-process proc))
4890	  (ignore-errors (delete-file tmp-name))))
4891
4892      ;; Process connection type.
4893      (when (and (tramp--test-sh-p)
4894		 (not (tramp-direct-async-process-p))
4895		 ;; `executable-find' has changed the number of
4896		 ;; parameters in Emacs 27.1, so we use `apply' for
4897		 ;; older Emacsen.
4898		 (ignore-errors
4899		   (with-no-warnings
4900		     (apply #'executable-find '("hexdump" remote)))))
4901	(dolist (connection-type '(nil pipe t pty))
4902	  ;; `process-connection-type' is taken when
4903	  ;; `:connection-type' is nil.
4904	  (dolist (process-connection-type
4905		   (unless connection-type '(nil pipe t pty)))
4906	    (unwind-protect
4907		(with-temp-buffer
4908		  (setq proc
4909			(with-no-warnings
4910			  (make-process
4911			   :name
4912			   (format "test7-%s-%s"
4913				   connection-type process-connection-type)
4914			   :buffer (current-buffer)
4915			   :connection-type connection-type
4916			   :command '("hexdump" "-v" "-e" "/1 \"%02X\n\"")
4917			   :file-handler t)))
4918		  (should (processp proc))
4919		  (should (equal (process-status proc) 'run))
4920		  (process-send-string proc "foo\r\n")
4921		  (process-send-eof proc)
4922		  ;; Read output.
4923		  (with-timeout (10 (tramp--test-timeout-handler))
4924		    (while (< (- (point-max) (point-min))
4925			      (length "66\n6F\n6F\n0D\n0A\n"))
4926		      (while (accept-process-output proc 0 nil t))))
4927		  (should
4928		   (string-match-p
4929		    (if (and (memq (or connection-type process-connection-type)
4930			           '(nil pipe))
4931                             (not (tramp--test-macos-p)))
4932                        ;; On macOS, there is always newline conversion.
4933			;; "telnet" converts \r to <CR><NUL> if `crlf'
4934			;; flag is FALSE.  See telnet(1) man page.
4935			"66\n6F\n6F\n0D\\(\n00\\)?\n0A\n"
4936		      "66\n6F\n6F\n0A\\(\n00\\)?\n0A\n")
4937		    (buffer-string))))
4938
4939	      ;; Cleanup.
4940	      (ignore-errors (delete-process proc)))))))))
4941
4942(tramp--test--deftest-direct-async-process tramp-test30-make-process
4943  "Check direct async `make-process'.")
4944
4945(ert-deftest tramp-test31-interrupt-process ()
4946  "Check `interrupt-process'."
4947  :tags (append '(:expensive-test :tramp-asynchronous-processes)
4948		(and (or (getenv "EMACS_HYDRA_CI") (getenv "EMACS_EMBA_CI"))
4949		     '(:unstable)))
4950  (skip-unless (tramp--test-enabled))
4951  (skip-unless (tramp--test-sh-p))
4952  (skip-unless (not (tramp--test-crypt-p)))
4953
4954  ;; We must use `file-truename' for the temporary directory, in
4955  ;; order to establish the connection prior running an asynchronous
4956  ;; process.
4957  (let ((default-directory (file-truename tramp-test-temporary-file-directory))
4958	(delete-exited-processes t)
4959	kill-buffer-query-functions proc)
4960    (unwind-protect
4961	(with-temp-buffer
4962	  (setq proc (start-file-process-shell-command
4963		      "test" (current-buffer)
4964		      "trap 'echo boom; exit 1' 2; sleep 100"))
4965	  (should (processp proc))
4966	  (should (process-live-p proc))
4967	  (should (equal (process-status proc) 'run))
4968	  (should (numberp (process-get proc 'remote-pid)))
4969	  (should (interrupt-process proc))
4970	  ;; Let the process accept the interrupt.
4971	  (with-timeout (10 (tramp--test-timeout-handler))
4972	    (while (process-live-p proc)
4973	      (while (accept-process-output proc 0 nil t))))
4974	  (should-not (process-live-p proc))
4975	  ;; An interrupted process cannot be interrupted, again.
4976	  (should-error
4977	   (interrupt-process proc)
4978	   :type 'error))
4979
4980      ;; Cleanup.
4981      (ignore-errors (delete-process proc)))))
4982
4983(defun tramp--test-async-shell-command
4984    (command output-buffer &optional error-buffer input)
4985  "Like `async-shell-command', reading the output.
4986INPUT, if non-nil, is a string sent to the process."
4987  (let ((proc (async-shell-command command output-buffer error-buffer))
4988	(delete-exited-processes t))
4989    (cl-letf (((symbol-function #'shell-command-sentinel) #'ignore))
4990      (when (stringp input)
4991	(process-send-string proc input))
4992      (with-timeout
4993	  ((if (getenv "EMACS_EMBA_CI") 30 10) (tramp--test-timeout-handler))
4994	(while
4995	    (or (accept-process-output proc nil nil t) (process-live-p proc))))
4996      (accept-process-output proc nil nil t))))
4997
4998(defun tramp--test-shell-command-to-string-asynchronously (command)
4999  "Like `shell-command-to-string', but for asynchronous processes."
5000  (with-temp-buffer
5001    (tramp--test-async-shell-command command (current-buffer))
5002    (buffer-substring-no-properties (point-min) (point-max))))
5003
5004(ert-deftest tramp-test32-shell-command ()
5005  "Check `shell-command'."
5006  :tags '(:expensive-test)
5007  (skip-unless (tramp--test-enabled))
5008  (skip-unless (tramp--test-supports-processes-p))
5009  ;; Prior Emacs 27, `shell-file-name' was hard coded as "/bin/sh" for
5010  ;; remote processes in Emacs.  That doesn't work for tramp-adb.el.
5011  (when (tramp--test-adb-p)
5012    (skip-unless (tramp--test-emacs27-p)))
5013
5014  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
5015    (let ((tmp-name (tramp--test-make-temp-name nil quoted))
5016	  (default-directory tramp-test-temporary-file-directory)
5017	  ;; Suppress nasty messages.
5018	  (inhibit-message t)
5019	  kill-buffer-query-functions)
5020
5021      (dolist (this-shell-command
5022	       (append
5023		;; Synchronously.
5024		'(shell-command)
5025		;; Asynchronously.
5026		(and (tramp--test-asynchronous-processes-p)
5027		     '(tramp--test-async-shell-command))))
5028
5029	;; Test ordinary `{async-}shell-command'.
5030	(unwind-protect
5031	    (with-temp-buffer
5032	      (write-region "foo" nil tmp-name)
5033	      (should (file-exists-p tmp-name))
5034	      (funcall
5035	       this-shell-command
5036	       (format "ls %s" (file-name-nondirectory tmp-name))
5037	       (current-buffer))
5038	      ;; "ls" could produce colorized output.
5039	      (goto-char (point-min))
5040	      (while
5041		  (re-search-forward tramp-display-escape-sequence-regexp nil t)
5042		(replace-match "" nil nil))
5043	      (should
5044	       (string-equal
5045		(format "%s\n" (file-name-nondirectory tmp-name))
5046		(buffer-string))))
5047
5048	  ;; Cleanup.
5049	  (ignore-errors (delete-file tmp-name)))
5050
5051	;; Test `{async-}shell-command' with error buffer.
5052	(unless (tramp-direct-async-process-p)
5053	  (let ((stderr (generate-new-buffer "*stderr*")))
5054	    (unwind-protect
5055		(with-temp-buffer
5056		  (funcall
5057		   this-shell-command
5058		   "echo foo >&2; echo bar" (current-buffer) stderr)
5059		  (should (string-equal "bar\n" (buffer-string)))
5060		  ;; Check stderr.
5061		  (should
5062		   (string-equal "foo\n" (tramp-get-buffer-string stderr))))
5063
5064	      ;; Cleanup.
5065	      (ignore-errors (kill-buffer stderr))))))
5066
5067      ;; Test sending string to `async-shell-command'.
5068      (when (tramp--test-asynchronous-processes-p)
5069	(unwind-protect
5070	    (with-temp-buffer
5071	      (write-region "foo" nil tmp-name)
5072	      (should (file-exists-p tmp-name))
5073	      (tramp--test-async-shell-command
5074	       "read line; ls $line" (current-buffer) nil
5075	       ;; String to be sent.
5076	       (format "%s\n" (file-name-nondirectory tmp-name)))
5077	      (should
5078	       (string-equal
5079		;; tramp-adb.el echoes, so we must add the string.
5080		(if (and (tramp--test-adb-p)
5081			 (not (tramp-direct-async-process-p)))
5082		    (format
5083		     "%s\n%s\n"
5084		     (file-name-nondirectory tmp-name)
5085		     (file-name-nondirectory tmp-name))
5086		  (format "%s\n" (file-name-nondirectory tmp-name)))
5087		(buffer-string))))
5088
5089	  ;; Cleanup.
5090	  (ignore-errors (delete-file tmp-name))))))
5091
5092  ;; Test `async-shell-command-width'.  It exists since Emacs 26.1,
5093  ;; but seems to work since Emacs 27.1 only.
5094  (when (and (tramp--test-asynchronous-processes-p)
5095	     (tramp--test-sh-p) (tramp--test-emacs27-p))
5096    (let* ((async-shell-command-width 1024)
5097	   (default-directory tramp-test-temporary-file-directory)
5098	   (cols (ignore-errors
5099		   (read (tramp--test-shell-command-to-string-asynchronously
5100			  "tput cols")))))
5101      (when (natnump cols)
5102	(should (= cols async-shell-command-width))))))
5103
5104(tramp--test--deftest-direct-async-process tramp-test32-shell-command
5105  "Check direct async `shell-command'." 'unstable)
5106
5107;; This test is inspired by Bug#39067.
5108(ert-deftest tramp-test32-shell-command-dont-erase-buffer ()
5109  "Check `shell-command-dont-erase-buffer'."
5110  ;; As long as Bug#40896 is not solved both in simple.el and Tramp,
5111  ;; this test cannot run properly.
5112  :tags '(:expensive-test :unstable)
5113  (skip-unless (tramp--test-enabled))
5114  (skip-unless nil)
5115  (skip-unless (tramp--test-supports-processes-p))
5116  ;; Prior Emacs 27, `shell-command-dont-erase-buffer' wasn't working properly.
5117  (skip-unless (tramp--test-emacs27-p))
5118
5119  ;; (message "   s-c-d-e-b current-buffer buffer-string point")
5120  ;; (message "===============================================")
5121
5122  ;;    s-c-d-e-b current-buffer buffer-string point
5123  ;; ===============================================
5124  ;;          nil              t    foobazzbar     4 x
5125  ;;          nil            nil          bazz     5
5126  ;; -----------------------------------------------
5127  ;;        erase              t          bazz     1 x
5128  ;;        erase            nil          bazz     5
5129  ;; -----------------------------------------------
5130  ;; beg-last-out              t    foobazzbar     4 x
5131  ;; beg-last-out            nil    foobarbazz     7
5132  ;; -----------------------------------------------
5133  ;; end-last-out              t    foobazzbar     4
5134  ;; end-last-out            nil    foobazzbar    11
5135  ;; -----------------------------------------------
5136  ;;   save-point              t    foobazzbar     4 x
5137  ;;   save-point            nil    foobarbazz     4 x
5138  ;; -----------------------------------------------
5139  ;;       random              t    foobazzbar     4
5140  ;;       random            nil    foobazzbar    11
5141  ;; -----------------------------------------------
5142
5143  (let (;; Suppress nasty messages.
5144	(inhibit-message t)
5145	buffer kill-buffer-query-functions)
5146    ;; We check both the local and remote case, in order to guarantee
5147    ;; that they behave similar.
5148    (dolist (default-directory
5149	      `(,temporary-file-directory ,tramp-test-temporary-file-directory))
5150      ;; These are the possible values of `shell-command-dont-erase-buffer'.
5151      ;; `random' is taken as non-nil value without special meaning.
5152      (dolist (shell-command-dont-erase-buffer
5153	       '(nil erase beg-last-out end-last-out save-point random))
5154	;; `shell-command' might work over the current buffer, or not.
5155	(dolist (current '(t nil))
5156	  (with-temp-buffer
5157	    ;; We insert the string "foobar" into an empty buffer.
5158	    ;; Point is set between "foo" and "bar".
5159	    (setq buffer (current-buffer))
5160	    (insert "foobar")
5161	    (goto-char (- (point) 3))
5162	    (should (string-equal "foobar" (buffer-string)))
5163	    (should (string-equal "foo" (buffer-substring (point-min) (point))))
5164	    (should (string-equal "bar" (buffer-substring (point) (point-max))))
5165
5166	    ;; Apply `shell-command'.  It shall output the string
5167	    ;; "bazz".  Messages in the *Messages* buffer are
5168	    ;; suppressed.
5169            (let (message-log-max)
5170	      (if current
5171		  (shell-command "echo -n bazz" (current-buffer))
5172		(with-temp-buffer (shell-command "echo -n bazz" buffer))))
5173
5174	  ;;   (message
5175	  ;;    "%12s %14s %13s %5d"
5176	  ;;    shell-command-dont-erase-buffer current (buffer-string) (point))))
5177	  ;; (message "-----------------------------------------------")))))
5178
5179	    ;; Check result.
5180	    (cond
5181	     (current
5182	      ;; String is inserted at point, and point is preserved
5183	      ;; unless dictated otherwise.
5184	      (cond
5185	       ((null shell-command-dont-erase-buffer)
5186		(should (string-equal "foobazzbar" (buffer-string)))
5187		(should (= 4 (point))))
5188	       ((eq shell-command-dont-erase-buffer 'erase)
5189		(should (string-equal "bazz" (buffer-string)))
5190		(should (= 1 (point))))
5191	       ((eq shell-command-dont-erase-buffer 'beg-last-out)
5192		(should (string-equal "foobazzbar" (buffer-string)))
5193		(should (= 4 (point))))
5194	       ;; Bug#40896
5195	       ;; ((eq shell-command-dont-erase-buffer 'end-last-out)
5196	       ;; 	(should (string-equal "foobazzbar" (buffer-string)))
5197	       ;; 	(should (= 7 (point))))
5198	       ((eq shell-command-dont-erase-buffer 'save-point)
5199		(should (string-equal "foobazzbar" (buffer-string)))
5200		(should (= 4 (point))))
5201	       ;; Bug#40896
5202	       ;; ((eq shell-command-dont-erase-buffer 'random)
5203	       ;; 	(should (string-equal "foobazzbar" (buffer-string)))
5204	       ;; 	(should (= 7 (point))))))
5205	       ))
5206
5207	     (t ;; not current buffer
5208	      ;; String is appended, and point is at point-max unless
5209	      ;; dictated otherwise.
5210	      (cond
5211	       ((null shell-command-dont-erase-buffer)
5212		(should (string-equal "bazz" (buffer-string)))
5213		(should (= 5 (point))))
5214	       ((eq shell-command-dont-erase-buffer 'erase)
5215		(should (string-equal "bazz" (buffer-string)))
5216		(should (= 5 (point))))
5217	       ((eq shell-command-dont-erase-buffer 'beg-last-out)
5218		(should (string-equal "foobarbazz" (buffer-string)))
5219		(should (= 7 (point))))
5220	       ;; ;; Bug#40896
5221	       ;; ((eq shell-command-dont-erase-buffer 'end-last-out)
5222	       ;; 	(should (string-equal "foobarbazz" (buffer-string)))
5223	       ;; 	(should (= 11 (point))))
5224	       ((eq shell-command-dont-erase-buffer 'save-point)
5225		(should (string-equal "foobarbazz" (buffer-string)))
5226		(should (= 4 (point))))
5227	       ;; ;; Bug#40896
5228	       ;; ((eq shell-command-dont-erase-buffer 'random)
5229	       ;; 	(should (string-equal "foobarbazz" (buffer-string)))
5230	       ;; 	(should (= 11 (point)))))))))))))
5231	       )))))))))
5232
5233;; This test is inspired by Bug#23952.
5234(ert-deftest tramp-test33-environment-variables ()
5235  "Check that remote processes set / unset environment variables properly."
5236  :tags '(:expensive-test)
5237  (skip-unless (tramp--test-enabled))
5238  (skip-unless (tramp--test-sh-p))
5239  (skip-unless (not (tramp--test-crypt-p)))
5240
5241  (dolist (this-shell-command-to-string
5242	   (append
5243	    ;; Synchronously.
5244	    '(shell-command-to-string)
5245	    ;; Asynchronously.
5246	    (and (tramp--test-asynchronous-processes-p)
5247		 '(tramp--test-shell-command-to-string-asynchronously))))
5248
5249    (let ((default-directory tramp-test-temporary-file-directory)
5250	  (shell-file-name "/bin/sh")
5251	  (envvar (concat "VAR_" (upcase (md5 (current-time-string)))))
5252	  kill-buffer-query-functions)
5253
5254      ;; Check INSIDE_EMACS.
5255      (setenv "INSIDE_EMACS")
5256      (should
5257       (string-equal
5258	(format "%s,tramp:%s\n" emacs-version tramp-version)
5259	(funcall this-shell-command-to-string "echo \"${INSIDE_EMACS:-bla}\"")))
5260      (let ((process-environment
5261	     (cons (format "INSIDE_EMACS=%s,foo" emacs-version)
5262		   process-environment)))
5263	(should
5264	 (string-equal
5265	  (format "%s,foo,tramp:%s\n" emacs-version tramp-version)
5266	  (funcall
5267	   this-shell-command-to-string "echo \"${INSIDE_EMACS:-bla}\""))))
5268
5269      ;; Set a value.
5270      (let ((process-environment
5271	     (cons (concat envvar "=foo") process-environment)))
5272	;; Default value.
5273	(should
5274	 (string-match-p
5275	  "foo"
5276	  (funcall
5277	   this-shell-command-to-string
5278	   (format "echo \"${%s:-bla}\"" envvar)))))
5279
5280      ;; Set the empty value.
5281      (let ((process-environment
5282	     (cons (concat envvar "=") process-environment)))
5283	;; Value is null.
5284	(should
5285	 (string-match-p
5286	  "bla"
5287	  (funcall
5288	   this-shell-command-to-string (format "echo \"${%s:-bla}\"" envvar))))
5289	;; Variable is set.
5290	(should
5291	 (string-match-p
5292	  (regexp-quote envvar)
5293	  (funcall this-shell-command-to-string "set"))))
5294
5295      (unless (tramp-direct-async-process-p)
5296	;; We force a reconnect, in order to have a clean environment.
5297	(tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)
5298	;; Unset the variable.
5299	(let ((tramp-remote-process-environment
5300	       (cons (concat envvar "=foo") tramp-remote-process-environment)))
5301	  ;; Set the initial value, we want to unset below.
5302	  (should
5303	   (string-match-p
5304	    "foo"
5305	    (funcall
5306	     this-shell-command-to-string
5307	     (format "echo \"${%s:-bla}\"" envvar))))
5308	  (let ((process-environment (cons envvar process-environment)))
5309	    ;; Variable is unset.
5310	    (should
5311	     (string-match-p
5312	      "bla"
5313	      (funcall
5314	       this-shell-command-to-string
5315	       (format "echo \"${%s:-bla}\"" envvar))))
5316	    ;; Variable is unset.
5317	    (should-not
5318	     (string-match-p
5319	      (regexp-quote envvar)
5320	      ;; We must remove PS1, the output is truncated otherwise.
5321	      ;; We must suppress "_=VAR...".
5322	      (funcall
5323	       this-shell-command-to-string
5324	       "printenv | grep -v PS1 | grep -v _=")))))))))
5325
5326(tramp--test--deftest-direct-async-process tramp-test33-environment-variables
5327  "Check that remote processes set / unset environment variables properly.
5328Use direct async.")
5329
5330;; This test is inspired by Bug#27009.
5331(ert-deftest tramp-test33-environment-variables-and-port-numbers ()
5332  "Check that two connections with separate ports are different."
5333  (skip-unless (tramp--test-enabled))
5334  ;; We test it only for the mock-up connection; otherwise there might
5335  ;; be problems with the used ports.
5336  (skip-unless (and (eq tramp-syntax 'default) (tramp--test-mock-p)))
5337  (skip-unless (not (tramp--test-crypt-p)))
5338
5339  ;; We force a reconnect, in order to have a clean environment.
5340  (dolist (dir `(,tramp-test-temporary-file-directory
5341		 "/mock:localhost#11111:" "/mock:localhost#22222:"))
5342    (tramp-cleanup-connection
5343     (tramp-dissect-file-name dir) 'keep-debug 'keep-password))
5344
5345  (unwind-protect
5346      (dolist (port '(11111 22222))
5347	(let* ((default-directory
5348		 (format "/mock:localhost#%d:%s" port temporary-file-directory))
5349	       (shell-file-name "/bin/sh")
5350	       (envvar (concat "VAR_" (upcase (md5 (current-time-string)))))
5351	       ;; We cannot use `process-environment', because this
5352	       ;; would be applied in `process-file'.
5353	       (tramp-remote-process-environment
5354		(cons
5355		 (format "%s=%d" envvar port)
5356		 tramp-remote-process-environment)))
5357	  (should
5358	   (string-match-p
5359	    (number-to-string port)
5360	    (shell-command-to-string (format "echo $%s" envvar))))))
5361
5362    ;; Cleanup.
5363    (dolist (dir '("/mock:localhost#11111:" "/mock:localhost#22222:"))
5364      (tramp-cleanup-connection (tramp-dissect-file-name dir)))))
5365
5366;; Connection-local variables are enabled per default since Emacs 27.1.
5367(ert-deftest tramp-test34-connection-local-variables ()
5368  "Check that connection-local variables are enabled."
5369  :tags '(:expensive-test)
5370  (skip-unless (tramp--test-enabled))
5371  ;; Since Emacs 27.1.
5372  (skip-unless (fboundp 'with-connection-local-variables))
5373
5374  (let* ((default-directory tramp-test-temporary-file-directory)
5375	 (tmp-name1 (tramp--test-make-temp-name))
5376	 (tmp-name2 (expand-file-name "foo" tmp-name1))
5377	 (enable-local-variables :all)
5378	 (enable-remote-dir-locals t)
5379         (inhibit-message t)
5380	 kill-buffer-query-functions
5381	 connection-local-profile-alist connection-local-criteria-alist)
5382    (unwind-protect
5383	(progn
5384	  (make-directory tmp-name1)
5385          (should (file-directory-p tmp-name1))
5386
5387	  ;; `local-variable' is buffer-local due to explicit setting.
5388	  (with-no-warnings
5389	   (defvar-local local-variable 'buffer))
5390	  (with-temp-buffer
5391	    (should (eq local-variable 'buffer)))
5392
5393	  ;; `local-variable' is connection-local due to Tramp.
5394	  (write-region "foo" nil tmp-name2)
5395	  (should (file-exists-p tmp-name2))
5396	  (connection-local-set-profile-variables
5397	   'local-variable-profile
5398	   '((local-variable . connect)))
5399	  (connection-local-set-profiles
5400	   `(:application tramp
5401	     :protocol ,(file-remote-p default-directory 'method)
5402	     :user ,(file-remote-p default-directory 'user)
5403	     :machine ,(file-remote-p default-directory 'host))
5404	   'local-variable-profile)
5405	  (with-current-buffer (find-file-noselect tmp-name2)
5406	    (should (eq local-variable 'connect))
5407	    (kill-buffer (current-buffer)))
5408
5409	  ;; `local-variable' is dir-local due to existence of .dir-locals.el.
5410	  (write-region
5411	   "((nil . ((local-variable . dir))))" nil
5412	   (expand-file-name ".dir-locals.el" tmp-name1))
5413	  (should (file-exists-p (expand-file-name ".dir-locals.el" tmp-name1)))
5414	  (with-current-buffer (find-file-noselect tmp-name2)
5415	    (should (eq local-variable 'dir))
5416	    (kill-buffer (current-buffer)))
5417
5418	  ;; `local-variable' is file-local due to specifying as file variable.
5419	  (write-region
5420	   "-*- mode: comint; local-variable: file; -*-" nil tmp-name2)
5421          (should (file-exists-p tmp-name2))
5422	  (with-current-buffer (find-file-noselect tmp-name2)
5423	    (should (eq local-variable 'file))
5424	    (kill-buffer (current-buffer))))
5425
5426      ;; Cleanup.
5427      (ignore-errors (delete-directory tmp-name1 'recursive)))))
5428
5429(ert-deftest tramp-test34-explicit-shell-file-name ()
5430  "Check that connection-local `explicit-shell-file-name' is set."
5431  :tags '(:expensive-test :tramp-asynchronous-processes)
5432  (skip-unless (tramp--test-enabled))
5433  (skip-unless (tramp--test-supports-processes-p))
5434  ;; Prior Emacs 27, `shell-file-name' was hard coded as "/bin/sh" for
5435  ;; remote processes in Emacs.  That doesn't work for tramp-adb.el.
5436  (when (tramp--test-adb-p)
5437    (skip-unless (tramp--test-emacs27-p)))
5438
5439  (let ((default-directory tramp-test-temporary-file-directory)
5440	explicit-shell-file-name kill-buffer-query-functions
5441	connection-local-profile-alist connection-local-criteria-alist)
5442    (unwind-protect
5443	(progn
5444	  ;; `shell-mode' would ruin our test, because it deletes all
5445	  ;; buffer local variables.  Not needed in Emacs 27.1.
5446	  (put 'explicit-shell-file-name 'permanent-local t)
5447	  (connection-local-set-profile-variables
5448	   'remote-sh
5449	   `((explicit-shell-file-name . ,(tramp--test-shell-file-name))
5450	     (explicit-sh-args . ("-c" "echo foo"))))
5451	  (connection-local-set-profiles
5452	   `(:application tramp
5453	     :protocol ,(file-remote-p default-directory 'method)
5454	     :user ,(file-remote-p default-directory 'user)
5455	     :machine ,(file-remote-p default-directory 'host))
5456	   'remote-sh)
5457	  (put 'explicit-shell-file-name 'safe-local-variable #'identity)
5458	  (put 'explicit-sh-args 'safe-local-variable #'identity)
5459
5460	  ;; Run `shell' interactively.  Since the default directory
5461	  ;; is remote, `explicit-shell-file-name' shall be set in
5462	  ;; order to avoid a question.  `explicit-sh-args' echoes the
5463	  ;; test data.
5464	  (with-current-buffer (get-buffer-create "*shell*")
5465	    (ignore-errors (kill-process (get-buffer-process (current-buffer))))
5466	    (should-not explicit-shell-file-name)
5467	    (call-interactively #'shell)
5468	    (with-timeout (10)
5469	      (while (accept-process-output
5470		      (get-buffer-process (current-buffer)) nil nil t)))
5471	    (should (string-match-p "^foo$" (buffer-string)))))
5472
5473      ;; Cleanup.
5474      (put 'explicit-shell-file-name 'permanent-local nil)
5475      (kill-buffer "*shell*"))))
5476
5477;; `exec-path' was introduced in Emacs 27.1.  `executable-find' has
5478;; changed the number of parameters, so we use `apply' for older
5479;; Emacsen.
5480(ert-deftest tramp-test35-exec-path ()
5481  "Check `exec-path' and `executable-find'."
5482  (skip-unless (tramp--test-enabled))
5483  (skip-unless (tramp--test-supports-processes-p))
5484  (skip-unless (tramp--test-supports-set-file-modes-p))
5485  ;; Since Emacs 27.1.
5486  (skip-unless (fboundp 'exec-path))
5487
5488  (let ((tmp-name (tramp--test-make-temp-name))
5489	(default-directory tramp-test-temporary-file-directory))
5490    (unwind-protect
5491	(progn
5492	  (should (consp (with-no-warnings (exec-path))))
5493	  ;; Last element is the `exec-directory'.
5494	  (should
5495	   (string-equal
5496	    (car (last (with-no-warnings (exec-path))))
5497	    (file-remote-p default-directory 'localname)))
5498	  ;; The shell "sh" shall always exist.
5499	  (should (apply #'executable-find '("sh" remote)))
5500	  ;; Since the last element in `exec-path' is the current
5501	  ;; directory, an executable file in that directory will be
5502	  ;; found.
5503	  (write-region "foo" nil tmp-name)
5504	  (should (file-exists-p tmp-name))
5505
5506	  (set-file-modes tmp-name #o777)
5507	  (should (file-executable-p tmp-name))
5508	  (should
5509	   (string-equal
5510	    (apply
5511	     #'executable-find `(,(file-name-nondirectory tmp-name) remote))
5512	    (file-remote-p tmp-name 'localname)))
5513	  (should-not
5514	   (apply
5515	    #'executable-find
5516	    `(,(concat (file-name-nondirectory tmp-name) "foo") remote))))
5517
5518      ;; Cleanup.
5519      (ignore-errors (delete-file tmp-name)))))
5520
5521;; This test is inspired by Bug#33781.
5522;; `exec-path' was introduced in Emacs 27.1.  `executable-find' has
5523;; changed the number of parameters, so we use `apply' for older
5524;; Emacsen.
5525(ert-deftest tramp-test35-remote-path ()
5526  "Check loooong `tramp-remote-path'."
5527  (skip-unless (tramp--test-enabled))
5528  (skip-unless (tramp--test-sh-p))
5529  (skip-unless (not (tramp--test-crypt-p)))
5530  ;; Since Emacs 27.1.
5531  (skip-unless (fboundp 'exec-path))
5532
5533  (let* ((tmp-name (tramp--test-make-temp-name))
5534	 (default-directory tramp-test-temporary-file-directory)
5535         (orig-exec-path (with-no-warnings (exec-path)))
5536         (tramp-remote-path tramp-remote-path)
5537	 (orig-tramp-remote-path tramp-remote-path)
5538	 path)
5539    (unwind-protect
5540	(progn
5541          ;; Non existing directories are removed.
5542          (setq tramp-remote-path
5543                (cons (file-remote-p tmp-name 'localname) tramp-remote-path))
5544          (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)
5545          (should (equal (with-no-warnings (exec-path)) orig-exec-path))
5546          (setq tramp-remote-path orig-tramp-remote-path)
5547
5548          ;; Double entries are removed.
5549          (setq tramp-remote-path (append '("/" "/") tramp-remote-path))
5550          (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)
5551          (should
5552	   (equal (with-no-warnings (exec-path)) (cons "/" orig-exec-path)))
5553          (setq tramp-remote-path orig-tramp-remote-path)
5554
5555          ;; We make a super long `tramp-remote-path'.
5556          (make-directory tmp-name)
5557          (should (file-directory-p tmp-name))
5558          (while (< (length (mapconcat #'identity orig-exec-path ":")) 5000)
5559            (let ((dir (make-temp-file (file-name-as-directory tmp-name) 'dir)))
5560              (should (file-directory-p dir))
5561              (setq tramp-remote-path
5562                    (append
5563		     tramp-remote-path `(,(file-remote-p dir 'localname)))
5564                    orig-exec-path
5565                    (append
5566		     (butlast orig-exec-path)
5567		     `(,(file-remote-p dir 'localname))
5568		     (last orig-exec-path)))))
5569          (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)
5570          (should (equal (with-no-warnings (exec-path)) orig-exec-path))
5571          ;; Ignore trailing newline.
5572	  (setq path (substring (shell-command-to-string "echo $PATH") nil -1))
5573	  ;; The shell doesn't handle such long strings.
5574	  (when (<= (length path)
5575		    (tramp-get-connection-property
5576		     tramp-test-vec "pipe-buf" 4096))
5577	    ;; The last element of `exec-path' is `exec-directory'.
5578            (should
5579	     (string-equal
5580	      path (mapconcat #'identity (butlast orig-exec-path) ":"))))
5581	  ;; The shell "sh" shall always exist.
5582	  (should (apply #'executable-find '("sh" remote))))
5583
5584      ;; Cleanup.
5585      (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)
5586      (setq tramp-remote-path orig-tramp-remote-path)
5587      (ignore-errors (delete-directory tmp-name 'recursive)))))
5588
5589(ert-deftest tramp-test36-vc-registered ()
5590  "Check `vc-registered'."
5591  :tags '(:expensive-test)
5592  (skip-unless (tramp--test-enabled))
5593  (skip-unless (tramp--test-sh-p))
5594  (skip-unless (not (tramp--test-crypt-p)))
5595
5596  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
5597    ;; We must use `file-truename' for the temporary directory, in
5598    ;; order to establish the connection prior running an asynchronous
5599    ;; process.
5600    (let* ((default-directory
5601	     (file-truename tramp-test-temporary-file-directory))
5602	   (tmp-name1 (tramp--test-make-temp-name nil quoted))
5603	   (tmp-name2 (expand-file-name "foo" tmp-name1))
5604	   (tramp-remote-process-environment tramp-remote-process-environment)
5605           (inhibit-message t)
5606	   (vc-handled-backends
5607	    (cond
5608	     ((tramp-find-executable
5609	       tramp-test-vec vc-git-program
5610	       (tramp-get-remote-path tramp-test-vec))
5611	      '(Git))
5612	     ((tramp-find-executable
5613	       tramp-test-vec vc-hg-program
5614	       (tramp-get-remote-path tramp-test-vec))
5615	      '(Hg))
5616	     ((tramp-find-executable
5617	       tramp-test-vec vc-bzr-program
5618	       (tramp-get-remote-path tramp-test-vec))
5619	      (setq tramp-remote-process-environment
5620		    (cons (format "BZR_HOME=%s"
5621				  (file-remote-p tmp-name1 'localname))
5622			  tramp-remote-process-environment))
5623	      ;; We must force a reconnect, in order to activate $BZR_HOME.
5624	      (tramp-cleanup-connection
5625	       tramp-test-vec 'keep-debug 'keep-password)
5626	      '(Bzr))
5627	     (t nil)))
5628	   ;; Suppress nasty messages.
5629	   (inhibit-message t))
5630      (skip-unless vc-handled-backends)
5631      (unless quoted (tramp--test-message "%s" vc-handled-backends))
5632
5633      (unwind-protect
5634	  (progn
5635	    (make-directory tmp-name1)
5636	    (write-region "foo" nil tmp-name2)
5637	    (should (file-directory-p tmp-name1))
5638	    (should (file-exists-p tmp-name2))
5639	    (should-not (vc-registered tmp-name1))
5640	    (should-not (vc-registered tmp-name2))
5641
5642	    (let ((default-directory tmp-name1))
5643	      ;; Create empty repository, and register the file.
5644	      ;; Sometimes, creation of repository fails (bzr!); we
5645	      ;; skip the test then.
5646	      (condition-case nil
5647		  (vc-create-repo (car vc-handled-backends))
5648		(error (ert-skip "`vc-create-repo' not supported")))
5649	      ;; The structure of VC-FILESET is not documented.  Let's
5650	      ;; hope it won't change.
5651	      (vc-register
5652	       (list (car vc-handled-backends)
5653		     (list (file-name-nondirectory tmp-name2))))
5654	      ;; vc-git uses an own process sentinel, Tramp's sentinel
5655	      ;; for flushing the cache isn't used.
5656	      (dired-uncache (concat (file-remote-p default-directory) "/"))
5657	      (should (vc-registered (file-name-nondirectory tmp-name2)))))
5658
5659	;; Cleanup.
5660	(ignore-errors (delete-directory tmp-name1 'recursive))))))
5661
5662(ert-deftest tramp-test37-make-auto-save-file-name ()
5663  "Check `make-auto-save-file-name'."
5664  (skip-unless (tramp--test-enabled))
5665
5666  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
5667    (let ((tmp-name1 (tramp--test-make-temp-name nil quoted))
5668	  (tmp-name2 (tramp--test-make-temp-name nil quoted))
5669	  tramp-allow-unsafe-temporary-files)
5670
5671      (unwind-protect
5672	  (progn
5673	    ;; Use default `auto-save-file-name-transforms' mechanism.
5674	    ;; It isn't prepared for `separate' syntax.
5675	    (unless (eq tramp-syntax 'separate)
5676	      (let (tramp-auto-save-directory)
5677		(with-temp-buffer
5678		  (setq buffer-file-name tmp-name1)
5679		  (should
5680		   (string-equal
5681		    (make-auto-save-file-name)
5682		    ;; This is taken from original `make-auto-save-file-name'.
5683		    ;; We call `convert-standard-filename', because on
5684		    ;; MS Windows the (local) colons must be replaced by
5685		    ;; exclamation marks.
5686		    (convert-standard-filename
5687		     (expand-file-name
5688		      (format
5689		       "#%s#"
5690		       (subst-char-in-string
5691			?/ ?! (replace-regexp-in-string "!" "!!" tmp-name1)))
5692		      temporary-file-directory)))))))
5693
5694	    ;; No mapping.
5695	    (let (tramp-auto-save-directory auto-save-file-name-transforms)
5696	      (with-temp-buffer
5697		(setq buffer-file-name tmp-name1)
5698		(should
5699		 (string-equal
5700		  (make-auto-save-file-name)
5701		  (funcall
5702		   (if quoted #'tramp-compat-file-name-quote #'identity)
5703		   (expand-file-name
5704		    (format "#%s#" (file-name-nondirectory tmp-name1))
5705		    tramp-test-temporary-file-directory))))))
5706
5707	    ;; Use default `tramp-auto-save-directory' mechanism.
5708	    ;; Ange-FTP doesn't care.
5709	    (unless (tramp--test-ange-ftp-p)
5710	      (let ((tramp-auto-save-directory tmp-name2))
5711		(with-temp-buffer
5712		  (setq buffer-file-name tmp-name1)
5713		  (should
5714		   (string-equal
5715		    (make-auto-save-file-name)
5716		    ;; This is taken from Tramp.
5717		    (expand-file-name
5718		     (format
5719		      "#%s#"
5720		      (tramp-subst-strs-in-string
5721		       '(("_" . "|")
5722			 ("/" . "_a")
5723			 (":" . "_b")
5724			 ("|" . "__")
5725			 ("[" . "_l")
5726			 ("]" . "_r"))
5727		       (tramp-compat-file-name-unquote tmp-name1)))
5728		     tmp-name2)))
5729		  (should (file-directory-p tmp-name2)))))
5730
5731	    ;; Relative file names shall work, too.  Ange-FTP doesn't care.
5732	    (unless (tramp--test-ange-ftp-p)
5733	      (let ((tramp-auto-save-directory "."))
5734		(with-temp-buffer
5735		  (setq buffer-file-name tmp-name1
5736			default-directory tmp-name2)
5737		  (should
5738		   (string-equal
5739		    (make-auto-save-file-name)
5740		    ;; This is taken from Tramp.
5741		    (expand-file-name
5742		     (format
5743		      "#%s#"
5744		      (tramp-subst-strs-in-string
5745		       '(("_" . "|")
5746			 ("/" . "_a")
5747			 (":" . "_b")
5748			 ("|" . "__")
5749			 ("[" . "_l")
5750			 ("]" . "_r"))
5751		       (tramp-compat-file-name-unquote tmp-name1)))
5752		     tmp-name2)))
5753		  (should (file-directory-p tmp-name2)))))
5754
5755	    ;; Create temporary file.  This shall check for sensible
5756	    ;; files, owned by root.
5757	    (let ((tramp-auto-save-directory temporary-file-directory))
5758	      (write-region "foo" nil tmp-name1)
5759	      (when (zerop (or (file-attribute-user-id
5760				(file-attributes tmp-name1))
5761			       tramp-unknown-id-integer))
5762		(with-temp-buffer
5763		  (setq buffer-file-name tmp-name1)
5764		  (tramp-cleanup-connection
5765		   tramp-test-vec 'keep-debug 'keep-password)
5766		  (let ((tramp-allow-unsafe-temporary-files t))
5767		    (should (stringp (make-auto-save-file-name))))
5768		  (tramp-cleanup-connection
5769		   tramp-test-vec 'keep-debug 'keep-password)
5770		  (cl-letf (((symbol-function #'yes-or-no-p) #'ignore))
5771		    (should-error
5772		     (make-auto-save-file-name)
5773		     :type 'file-error))
5774		  (tramp-cleanup-connection
5775		   tramp-test-vec 'keep-debug 'keep-password)
5776		  (cl-letf (((symbol-function #'yes-or-no-p)
5777			     #'tramp--test-always))
5778		    (should (stringp (make-auto-save-file-name))))))))
5779
5780	;; Cleanup.
5781	(ignore-errors (delete-file tmp-name1))
5782	(ignore-errors (delete-directory tmp-name2 'recursive))
5783	(tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)))))
5784
5785(ert-deftest tramp-test38-find-backup-file-name ()
5786  "Check `find-backup-file-name'."
5787  (skip-unless (tramp--test-enabled))
5788
5789  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
5790    (let ((tmp-name1 (tramp--test-make-temp-name nil quoted))
5791	  (tmp-name2 (tramp--test-make-temp-name nil quoted))
5792	  (ange-ftp-make-backup-files t)
5793	  tramp-allow-unsafe-temporary-files
5794	  ;; These settings are not used by Tramp, so we ignore them.
5795	  version-control delete-old-versions
5796	  (kept-old-versions (default-toplevel-value 'kept-old-versions))
5797	  (kept-new-versions (default-toplevel-value 'kept-new-versions)))
5798
5799      (unwind-protect
5800	  ;; Use default `backup-directory-alist' mechanism.
5801	  (let (backup-directory-alist tramp-backup-directory-alist)
5802	    (should
5803	     (equal
5804	      (find-backup-file-name tmp-name1)
5805	      (list
5806	       (funcall
5807		(if quoted #'tramp-compat-file-name-quote #'identity)
5808		(expand-file-name
5809		 (format "%s~" (file-name-nondirectory tmp-name1))
5810		 tramp-test-temporary-file-directory)))))))
5811
5812      (unwind-protect
5813	  ;; Map `backup-directory-alist'.
5814	  (let ((backup-directory-alist `(("." . ,tmp-name2)))
5815		tramp-backup-directory-alist)
5816	    (should
5817	     (equal
5818	      (find-backup-file-name tmp-name1)
5819	      (list
5820	       (funcall
5821		(if quoted #'tramp-compat-file-name-quote #'identity)
5822		(expand-file-name
5823		 (format
5824		  "%s~"
5825		  ;; This is taken from `make-backup-file-name-1'.  We
5826		  ;; call `convert-standard-filename', because on MS
5827		  ;; Windows the (local) colons must be replaced by
5828		  ;; exclamation marks.
5829		  (subst-char-in-string
5830		   ?/ ?!
5831		   (replace-regexp-in-string
5832		    "!" "!!" (convert-standard-filename tmp-name1))))
5833		 tmp-name2)))))
5834	    ;; The backup directory is created.
5835	    (should (file-directory-p tmp-name2)))
5836
5837	;; Cleanup.
5838	(ignore-errors (delete-directory tmp-name2 'recursive)))
5839
5840      (unwind-protect
5841	  ;; Map `tramp-backup-directory-alist'.  Ange-FTP doesn't care.
5842	  (unless (tramp--test-ange-ftp-p)
5843	    (let ((tramp-backup-directory-alist `(("." . ,tmp-name2)))
5844		  backup-directory-alist)
5845	      (should
5846	       (equal
5847		(find-backup-file-name tmp-name1)
5848		(list
5849		 (funcall
5850		  (if quoted #'tramp-compat-file-name-quote #'identity)
5851		  (expand-file-name
5852		   (format
5853		    "%s~"
5854		    ;; This is taken from `make-backup-file-name-1'.
5855		    ;; We call `convert-standard-filename', because on
5856		    ;; MS Windows the (local) colons must be replaced
5857		    ;; by exclamation marks.
5858		    (subst-char-in-string
5859		     ?/ ?!
5860		     (replace-regexp-in-string
5861		      "!" "!!" (convert-standard-filename tmp-name1))))
5862		   tmp-name2)))))
5863	      ;; The backup directory is created.
5864	      (should (file-directory-p tmp-name2))))
5865
5866	;; Cleanup.
5867	(ignore-errors (delete-directory tmp-name2 'recursive)))
5868
5869      (unwind-protect
5870	  ;; Map `tramp-backup-directory-alist' with local file name.
5871	  ;; Ange-FTP doesn't care.
5872	  (unless (tramp--test-ange-ftp-p)
5873	    (let ((tramp-backup-directory-alist
5874		   `(("." . ,(file-remote-p tmp-name2 'localname))))
5875		  backup-directory-alist)
5876	      (should
5877	       (equal
5878		(find-backup-file-name tmp-name1)
5879		(list
5880		 (funcall
5881		  (if quoted #'tramp-compat-file-name-quote #'identity)
5882		  (expand-file-name
5883		   (format
5884		    "%s~"
5885		    ;; This is taken from `make-backup-file-name-1'.
5886		    ;; We call `convert-standard-filename', because on
5887		    ;; MS Windows the (local) colons must be replaced
5888		    ;; by exclamation marks.
5889		    (subst-char-in-string
5890		     ?/ ?!
5891		     (replace-regexp-in-string
5892		      "!" "!!" (convert-standard-filename tmp-name1))))
5893		   tmp-name2)))))
5894	      ;; The backup directory is created.
5895	      (should (file-directory-p tmp-name2))))
5896
5897	;; Cleanup.
5898	(ignore-errors (delete-directory tmp-name2 'recursive)))
5899
5900      (unwind-protect
5901	  ;; Create temporary file.  This shall check for sensible
5902	  ;; files, owned by root.
5903	  (let ((backup-directory-alist `(("." . ,temporary-file-directory)))
5904		tramp-backup-directory-alist)
5905	    (write-region "foo" nil tmp-name1)
5906	    (when (zerop (or (file-attribute-user-id (file-attributes tmp-name1))
5907			     tramp-unknown-id-integer))
5908	      (tramp-cleanup-connection
5909	       tramp-test-vec 'keep-debug 'keep-password)
5910	      (let ((tramp-allow-unsafe-temporary-files t))
5911		(should (stringp (car (find-backup-file-name tmp-name1)))))
5912	      (tramp-cleanup-connection
5913	       tramp-test-vec 'keep-debug 'keep-password)
5914	      (cl-letf (((symbol-function #'yes-or-no-p) #'ignore))
5915		(should-error
5916		 (find-backup-file-name tmp-name1)
5917		 :type 'file-error))
5918	      (tramp-cleanup-connection
5919	       tramp-test-vec 'keep-debug 'keep-password)
5920	      (cl-letf (((symbol-function #'yes-or-no-p)
5921			 #'tramp--test-always))
5922		(should (stringp (car (find-backup-file-name tmp-name1)))))))
5923
5924	;; Cleanup.
5925	(ignore-errors (delete-file tmp-name1))
5926	(tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)))))
5927
5928;; The functions were introduced in Emacs 28.1.
5929(ert-deftest tramp-test39-make-lock-file-name ()
5930  "Check `make-lock-file-name', `lock-file', `unlock-file' and `file-locked-p'."
5931  (skip-unless (tramp--test-enabled))
5932  (skip-unless (not (tramp--test-ange-ftp-p)))
5933  ;; Since Emacs 28.1.
5934  (skip-unless (and (fboundp 'lock-file) (fboundp 'unlock-file)))
5935  (skip-unless (and (fboundp 'file-locked-p) (fboundp 'make-lock-file-name)))
5936
5937  ;; `lock-file', `unlock-file', `file-locked-p' and
5938  ;; `make-lock-file-name' exists since Emacs 28.1.  We don't want to
5939  ;; see compiler warnings for older Emacsen.
5940  (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil)))
5941    (let ((tmp-name1 (tramp--test-make-temp-name nil quoted))
5942	  (tmp-name2 (tramp--test-make-temp-name nil quoted))
5943	  (remote-file-name-inhibit-cache t)
5944	  (remote-file-name-inhibit-locks nil)
5945	  (create-lockfiles t)
5946	  tramp-allow-unsafe-temporary-files
5947          (inhibit-message t)
5948	  ;; tramp-rclone.el and tramp-sshfs.el cache the mounted files.
5949	  (tramp-fuse-unmount-on-cleanup t)
5950          auto-save-default
5951	  noninteractive)
5952
5953      (unwind-protect
5954	  (progn
5955	    ;; A simple file lock.
5956	    (should-not (with-no-warnings (file-locked-p tmp-name1)))
5957	    (with-no-warnings (lock-file tmp-name1))
5958	    (should (eq (with-no-warnings (file-locked-p tmp-name1)) t))
5959
5960	    ;; If it is locked already, nothing changes.
5961	    (with-no-warnings (lock-file tmp-name1))
5962	    (should (eq (with-no-warnings (file-locked-p tmp-name1)) t))
5963
5964            ;; `save-buffer' removes the lock.
5965            (with-temp-buffer
5966              (set-visited-file-name tmp-name1)
5967              (insert "foo")
5968              (save-buffer))
5969            (should-not (with-no-warnings (file-locked-p tmp-name1)))
5970	    (with-no-warnings (lock-file tmp-name1))
5971	    (should (eq (with-no-warnings (file-locked-p tmp-name1)) t))
5972
5973	    ;; A new connection changes process id, and also the
5974	    ;; lockname contents.
5975	    (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)
5976	    (should (stringp (with-no-warnings (file-locked-p tmp-name1))))
5977
5978	    ;; When `remote-file-name-inhibit-locks' is set, nothing happens.
5979	    (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)
5980	    (let ((remote-file-name-inhibit-locks t))
5981	      (with-no-warnings (lock-file tmp-name1))
5982	      (should-not (with-no-warnings (file-locked-p tmp-name1))))
5983
5984	    ;; When `lock-file-name-transforms' is set, another lock
5985	    ;; file is used.
5986	    (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)
5987	    (let ((lock-file-name-transforms `((".*" ,tmp-name2))))
5988	      (should
5989	       (string-equal
5990		(with-no-warnings (make-lock-file-name tmp-name1))
5991		(with-no-warnings (make-lock-file-name tmp-name2))))
5992	      (with-no-warnings (lock-file tmp-name1))
5993	      (should (eq (with-no-warnings (file-locked-p tmp-name1)) t))
5994	      (with-no-warnings (unlock-file tmp-name1))
5995	      (should-not (with-no-warnings (file-locked-p tmp-name1))))
5996
5997	    ;; Steal the file lock.
5998	    (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)
5999	    (cl-letf (((symbol-function #'read-char) (lambda (&rest _args) ?s)))
6000	      (with-no-warnings (lock-file tmp-name1)))
6001	    (should (eq (with-no-warnings (file-locked-p tmp-name1)) t))
6002
6003	    ;; Ignore the file lock.
6004	    (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)
6005	    (cl-letf (((symbol-function #'read-char) (lambda (&rest _args) ?p)))
6006	      (with-no-warnings (lock-file tmp-name1)))
6007	    (should (stringp (with-no-warnings (file-locked-p tmp-name1))))
6008
6009	    ;; Quit the file lock machinery.
6010	    (tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)
6011	    (cl-letf (((symbol-function #'read-char) (lambda (&rest _args) ?q)))
6012	      (with-no-warnings
6013		(should-error
6014		 (lock-file tmp-name1)
6015		 :type 'file-locked))
6016	      ;; The same for `write-region'.
6017	      (should-error
6018	       (write-region "foo" nil tmp-name1)
6019	       :type 'file-locked)
6020	      (should-error
6021	       (write-region "foo" nil tmp-name1 nil nil tmp-name1)
6022               :type 'file-locked)
6023	      ;; The same for `set-visited-file-name'.
6024              (with-temp-buffer
6025	        (should-error
6026                 (set-visited-file-name tmp-name1)
6027		 :type 'file-locked)))
6028	    (should (stringp (with-no-warnings (file-locked-p tmp-name1)))))
6029
6030	;; Cleanup.
6031	(ignore-errors (delete-file tmp-name1))
6032	(with-no-warnings (unlock-file tmp-name1))
6033	(with-no-warnings (unlock-file tmp-name2))
6034	(should-not (with-no-warnings (file-locked-p tmp-name1)))
6035	(should-not (with-no-warnings (file-locked-p tmp-name2))))
6036
6037      (unwind-protect
6038	  ;; Create temporary file.  This shall check for sensible
6039	  ;; files, owned by root.
6040	  (let ((lock-file-name-transforms auto-save-file-name-transforms))
6041	    (write-region "foo" nil tmp-name1)
6042	    (when (zerop (or (file-attribute-user-id (file-attributes tmp-name1))
6043			     tramp-unknown-id-integer))
6044	      (tramp-cleanup-connection
6045	       tramp-test-vec 'keep-debug 'keep-password)
6046	      (cl-letf (((symbol-function #'yes-or-no-p) #'ignore))
6047		(should-error
6048		 (write-region "foo" nil tmp-name1)
6049		 :type 'file-error))
6050	      (tramp-cleanup-connection
6051	       tramp-test-vec 'keep-debug 'keep-password)
6052	      (cl-letf (((symbol-function #'yes-or-no-p)
6053			 #'tramp--test-always))
6054		(write-region "foo" nil tmp-name1))))
6055
6056	;; Cleanup.
6057	(ignore-errors (delete-file tmp-name1))
6058	(tramp-cleanup-connection tramp-test-vec 'keep-debug 'keep-password)))))
6059
6060(ert-deftest tramp-test40-make-nearby-temp-file ()
6061  "Check `make-nearby-temp-file' and `temporary-file-directory'."
6062  (skip-unless (tramp--test-enabled))
6063  (skip-unless (not (tramp--test-ange-ftp-p)))
6064
6065  (let ((default-directory tramp-test-temporary-file-directory)
6066	tmp-file)
6067    ;; The remote host shall know a temporary file directory.
6068    (should (stringp (temporary-file-directory)))
6069    (should
6070     (string-equal
6071      (file-remote-p default-directory)
6072      (file-remote-p (temporary-file-directory))))
6073
6074    ;; The temporary file shall be located on the remote host.
6075    (setq tmp-file (make-nearby-temp-file "tramp-test"))
6076    (should (file-exists-p tmp-file))
6077    (should (file-regular-p tmp-file))
6078    (should
6079     (string-equal
6080      (file-remote-p default-directory)
6081      (file-remote-p tmp-file)))
6082    (delete-file tmp-file)
6083    (should-not (file-exists-p tmp-file))
6084
6085    (setq tmp-file (make-nearby-temp-file "tramp-test" 'dir))
6086    (should (file-exists-p tmp-file))
6087    (should (file-directory-p tmp-file))
6088    (delete-directory tmp-file)
6089    (should-not (file-exists-p tmp-file))))
6090
6091(defun tramp--test-emacs27-p ()
6092  "Check for Emacs version >= 27.1.
6093Some semantics has been changed for there, w/o new functions or
6094variables, so we check the Emacs version directly."
6095  (>= emacs-major-version 27))
6096
6097(defun tramp--test-emacs28-p ()
6098  "Check for Emacs version >= 28.1.
6099Some semantics has been changed for there, w/o new functions or
6100variables, so we check the Emacs version directly."
6101  (>= emacs-major-version 28))
6102
6103(defun tramp--test-emacs29-p ()
6104  "Check for Emacs version >= 29.1.
6105Some semantics has been changed for there, w/o new functions or
6106variables, so we check the Emacs version directly."
6107  (>= emacs-major-version 29))
6108
6109(defun tramp--test-adb-p ()
6110  "Check, whether the remote host runs Android.
6111This requires restrictions of file name syntax."
6112  (tramp-adb-file-name-p tramp-test-temporary-file-directory))
6113
6114(defun tramp--test-ange-ftp-p ()
6115  "Check, whether Ange-FTP is used."
6116  (eq
6117   (tramp-find-foreign-file-name-handler tramp-test-temporary-file-directory)
6118   'tramp-ftp-file-name-handler))
6119
6120(defun tramp--test-asynchronous-processes-p ()
6121  "Whether asynchronous processes tests are run.
6122This is used in tests which we dont't want to tag
6123`:tramp-asynchronous-processes' completely."
6124  (ert-select-tests
6125   (ert--stats-selector ert--current-run-stats)
6126   (list (make-ert-test :name (ert-test-name (ert-running-test))
6127                        :body nil :tags '(:tramp-asynchronous-processes)))))
6128
6129(defun tramp--test-crypt-p ()
6130  "Check, whether the remote directory is crypted."
6131  (tramp-crypt-file-name-p tramp-test-temporary-file-directory))
6132
6133(defun tramp--test-docker-p ()
6134  "Check, whether the docker method is used.
6135This does not support some special file names."
6136  (string-equal
6137   "docker" (file-remote-p tramp-test-temporary-file-directory 'method)))
6138
6139(defun tramp--test-expensive-test-p ()
6140  "Whether expensive tests are run.
6141This is used in tests which we dont't want to tag `:expensive'
6142completely."
6143  (ert-select-tests
6144   (ert--stats-selector ert--current-run-stats)
6145   (list (make-ert-test :name (ert-test-name (ert-running-test))
6146                        :body nil :tags '(:expensive-test)))))
6147
6148(defun tramp--test-ftp-p ()
6149  "Check, whether an FTP-like method is used.
6150This does not support globbing characters in file names (yet)."
6151  ;; Globbing characters are ??, ?* and ?\[.
6152  (string-match-p
6153   "ftp$" (file-remote-p tramp-test-temporary-file-directory 'method)))
6154
6155(defun tramp--test-fuse-p ()
6156  "Check, whether an FUSE file system isused."
6157  (or (tramp--test-rclone-p) (tramp--test-sshfs-p)))
6158
6159(defun tramp--test-gdrive-p ()
6160  "Check, whether the gdrive method is used."
6161  (string-equal
6162   "gdrive" (file-remote-p tramp-test-temporary-file-directory 'method)))
6163
6164(defun tramp--test-gvfs-p (&optional method)
6165  "Check, whether the remote host runs a GVFS based method.
6166This requires restrictions of file name syntax.
6167If optional METHOD is given, it is checked first."
6168  (or (member method tramp-gvfs-methods)
6169      (tramp-gvfs-file-name-p tramp-test-temporary-file-directory)))
6170
6171(defun tramp--test-hpux-p ()
6172  "Check, whether the remote host runs HP-UX.
6173Several special characters do not work properly there."
6174  ;; We must refill the cache.  `file-truename' does it.
6175  (file-truename tramp-test-temporary-file-directory)
6176  (tramp-check-remote-uname tramp-test-vec "^HP-UX"))
6177
6178(defun tramp--test-ksh-p ()
6179  "Check, whether the remote shell is ksh.
6180ksh93 makes some strange conversions of non-latin characters into
6181a $'' syntax."
6182  ;; We must refill the cache.  `file-truename' does it.
6183  (file-truename tramp-test-temporary-file-directory)
6184  (string-match-p
6185   "ksh$" (tramp-get-connection-property tramp-test-vec "remote-shell" "")))
6186
6187(defun tramp--test-macos-p ()
6188  "Check, whether the remote host runs macOS."
6189  ;; We must refill the cache.  `file-truename' does it.
6190  (file-truename tramp-test-temporary-file-directory)
6191  (tramp-check-remote-uname tramp-test-vec "Darwin"))
6192
6193(defun tramp--test-mock-p ()
6194  "Check, whether the mock method is used.
6195This does not support external Emacs calls."
6196  (string-equal
6197   "mock" (file-remote-p tramp-test-temporary-file-directory 'method)))
6198
6199(defun tramp--test-out-of-band-p ()
6200  "Check, whether an out-of-band method is used."
6201  (tramp-method-out-of-band-p tramp-test-vec 1))
6202
6203(defun tramp--test-rclone-p ()
6204  "Check, whether the remote host is offered by rclone.
6205This requires restrictions of file name syntax."
6206  (tramp-rclone-file-name-p tramp-test-temporary-file-directory))
6207
6208(defun tramp--test-rsync-p ()
6209  "Check, whether the rsync method is used.
6210This does not support special file names."
6211  (string-equal
6212   "rsync" (file-remote-p tramp-test-temporary-file-directory 'method)))
6213
6214(defun tramp--test-sh-p ()
6215  "Check, whether the remote host runs a based method from tramp-sh.el."
6216  (tramp-sh-file-name-handler-p tramp-test-vec))
6217
6218(defun tramp--test-sh-no-ls--dired-p ()
6219  "Check, whether the remote host runs a based method from tramp-sh.el.
6220Additionally, ls does not support \"--dired\"."
6221  (and (tramp--test-sh-p)
6222       (with-temp-buffer
6223	 ;; We must refill the cache.  `insert-directory' does it.
6224	 ;; This fails for tramp-crypt.el, so we ignore that.
6225	 (ignore-errors
6226	   (insert-directory tramp-test-temporary-file-directory "-al"))
6227	 (not (tramp-get-connection-property tramp-test-vec "ls--dired" nil)))))
6228
6229(defun tramp--test-share-p ()
6230  "Check, whether the method needs a share."
6231  (and (tramp--test-gvfs-p)
6232       (string-match-p
6233	"^\\(afp\\|davs?\\|smb\\)$"
6234	(file-remote-p tramp-test-temporary-file-directory 'method))))
6235
6236(defun tramp--test-sshfs-p ()
6237  "Check, whether the remote host is offered by sshfs.
6238This requires restrictions of file name syntax."
6239  (tramp-sshfs-file-name-p tramp-test-temporary-file-directory))
6240
6241(defun tramp--test-sudoedit-p ()
6242  "Check, whether the sudoedit method is used."
6243  (tramp-sudoedit-file-name-p tramp-test-temporary-file-directory))
6244
6245(defun tramp--test-telnet-p ()
6246  "Check, whether the telnet method is used.
6247This does not support special file names."
6248  (string-equal
6249   "telnet" (file-remote-p tramp-test-temporary-file-directory 'method)))
6250
6251(defun tramp--test-windows-nt-p ()
6252  "Check, whether the locale host runs MS Windows."
6253  (eq system-type 'windows-nt))
6254
6255(defun tramp--test-windows-nt-and-out-of-band-p ()
6256  "Check, whether the locale host runs MS Windows and an out-of-band method.
6257This does not support utf8 based file transfer."
6258  (and (tramp--test-windows-nt-p)
6259       (tramp--test-out-of-band-p)))
6260
6261(defun tramp--test-windows-nt-or-smb-p ()
6262  "Check, whether the locale or remote host runs MS Windows.
6263This requires restrictions of file name syntax."
6264  (or (tramp--test-windows-nt-p)
6265      (tramp--test-smb-p)))
6266
6267(defun tramp--test-smb-p ()
6268  "Check, whether the locale or remote host runs MS Windows.
6269This requires restrictions of file name syntax."
6270  (tramp-smb-file-name-p tramp-test-temporary-file-directory))
6271
6272(defun tramp--test-supports-processes-p ()
6273  "Return whether the method under test supports external processes."
6274  (and (or (tramp--test-adb-p) (tramp--test-sh-p) (tramp--test-sshfs-p))
6275       (not (tramp--test-crypt-p))))
6276
6277(defun tramp--test-supports-set-file-modes-p ()
6278  "Return whether the method under test supports setting file modes."
6279  ;; "smb" does not unless the SMB server supports "posix" extensions.
6280  ;; "adb" does not unless the Android device is rooted.
6281  (or (tramp--test-sh-p) (tramp--test-sshfs-p) (tramp--test-sudoedit-p)
6282      ;; Not all tramp-gvfs.el methods support changing the file mode.
6283      (and
6284       (tramp--test-gvfs-p)
6285       (string-match-p
6286	"ftp" (file-remote-p tramp-test-temporary-file-directory 'method)))))
6287
6288(defun tramp--test-check-files (&rest files)
6289  "Run a simple but comprehensive test over every file in FILES."
6290  ;; `filename-non-special' has been fixed in Emacs 27.1, see Bug#29579.
6291  (dolist (quoted
6292	   (if (and (tramp--test-expensive-test-p) (tramp--test-emacs27-p))
6293	       '(nil t) '(nil)))
6294    ;; We must use `file-truename' for the temporary directory,
6295    ;; because it could be located on a symlinked directory.  This
6296    ;; would let the test fail.
6297    (let* ((tramp-test-temporary-file-directory
6298	    (file-truename tramp-test-temporary-file-directory))
6299	   (tmp-name1 (tramp--test-make-temp-name nil quoted))
6300	   (tmp-name2 (tramp--test-make-temp-name 'local quoted))
6301	   (files
6302            (delq
6303             nil (mapcar (lambda (x) (unless (string-empty-p x) x)) files)))
6304	   (process-environment process-environment)
6305	   (sorted-files (sort (copy-sequence files) #'string-lessp))
6306	   buffer)
6307      (unwind-protect
6308	  (progn
6309	    (make-directory tmp-name1)
6310	    (make-directory tmp-name2)
6311
6312	    (dolist (elt files)
6313              ;(tramp--test-message "'%s'" elt)
6314	      (let* ((file1 (expand-file-name elt tmp-name1))
6315		     (file2 (expand-file-name elt tmp-name2))
6316		     (file3 (expand-file-name (concat elt "foo") tmp-name1)))
6317		(write-region elt nil file1)
6318		(should (file-exists-p file1))
6319
6320		;; Check file contents.
6321		(with-temp-buffer
6322		  (insert-file-contents file1)
6323		  (should (string-equal (buffer-string) elt)))
6324
6325		;; Copy file both directions.
6326		(copy-file file1 (file-name-as-directory tmp-name2))
6327		(should (file-exists-p file2))
6328		(delete-file file1)
6329		(should-not (file-exists-p file1))
6330		(copy-file file2 (file-name-as-directory tmp-name1))
6331		(should (file-exists-p file1))
6332
6333		(tramp--test-ignore-make-symbolic-link-error
6334		  (make-symbolic-link file1 file3)
6335		  (should (file-symlink-p file3))
6336		  (should
6337		   (string-equal
6338		    (expand-file-name file1) (file-truename file3)))
6339		  (should
6340		   (string-equal
6341		    (funcall
6342		     (if quoted #'tramp-compat-file-name-quote #'identity)
6343		     (file-attribute-type (file-attributes file3)))
6344		    (file-remote-p (file-truename file1) 'localname)))
6345		  ;; Check file contents.
6346		  (with-temp-buffer
6347		    (insert-file-contents file3)
6348		    (should (string-equal (buffer-string) elt)))
6349		  (delete-file file3))))
6350
6351	    ;; Check file names.
6352	    (should (equal (directory-files
6353			    tmp-name1 nil directory-files-no-dot-files-regexp)
6354			   sorted-files))
6355	    (should (equal (directory-files
6356			    tmp-name2 nil directory-files-no-dot-files-regexp)
6357			   sorted-files))
6358	    (should (equal (mapcar
6359			    #'car
6360			    (directory-files-and-attributes
6361			     tmp-name1 nil directory-files-no-dot-files-regexp))
6362			   sorted-files))
6363	    (should (equal (mapcar
6364			    #'car
6365			    (directory-files-and-attributes
6366			     tmp-name2 nil directory-files-no-dot-files-regexp))
6367			   sorted-files))
6368
6369	    ;; Check, that `insert-directory' works properly.
6370	    (with-current-buffer
6371		(setq buffer (dired-noselect tmp-name1 "--dired -al"))
6372	      (goto-char (point-min))
6373	      (while (not (eobp))
6374		(when-let ((name (dired-get-filename 'no-dir 'no-error)))
6375		  (unless
6376		      (string-match-p name directory-files-no-dot-files-regexp)
6377		    (should (member name files))))
6378		(forward-line 1)))
6379	    (kill-buffer buffer)
6380
6381	    ;; `substitute-in-file-name' could return different
6382	    ;; values.  For "adb", there could be strange file
6383	    ;; permissions preventing overwriting a file.  We don't
6384	    ;; care in this test case.
6385	    (dolist (elt files)
6386	      (let ((file1
6387		     (substitute-in-file-name (expand-file-name elt tmp-name1)))
6388		    (file2
6389		     (substitute-in-file-name
6390		      (expand-file-name elt tmp-name2))))
6391		(ignore-errors (write-region elt nil file1))
6392		(should (file-exists-p file1))
6393		(ignore-errors (write-region elt nil file2 nil 'nomessage))
6394		(should (file-exists-p file2))))
6395
6396	    (should (equal (directory-files
6397			    tmp-name1 nil directory-files-no-dot-files-regexp)
6398			   (directory-files
6399			    tmp-name2 nil directory-files-no-dot-files-regexp)))
6400
6401	    ;; Check directory creation.  We use a subdirectory "foo"
6402	    ;; in order to avoid conflicts with previous file name tests.
6403	    (dolist (elt files)
6404	      (let* ((elt1 (concat elt "foo"))
6405		     (file1 (expand-file-name (concat "foo/" elt) tmp-name1))
6406		     (file2 (expand-file-name elt file1))
6407		     (file3 (expand-file-name elt1 file1)))
6408		(make-directory file1 'parents)
6409		(should (file-directory-p file1))
6410		(write-region elt nil file2)
6411		(should (file-exists-p file2))
6412		(should
6413		 (equal
6414		  (directory-files
6415		   file1 nil directory-files-no-dot-files-regexp)
6416		  `(,elt)))
6417		(should
6418		 (equal
6419		  (caar (directory-files-and-attributes
6420			 file1 nil directory-files-no-dot-files-regexp))
6421		  elt))
6422
6423		;; Check symlink in `directory-files-and-attributes'.
6424		;; It does not work in the "smb" case, only relative
6425		;; symlinks to existing files are shown there.
6426		(tramp--test-ignore-make-symbolic-link-error
6427		  (unless (tramp--test-smb-p)
6428		    (make-symbolic-link file2 file3)
6429		    (should (file-symlink-p file3))
6430		    (should
6431		     (string-equal
6432		      (caar (directory-files-and-attributes
6433			     file1 nil (regexp-quote elt1)))
6434		      elt1))
6435		    (should
6436		     (string-equal
6437		      (funcall
6438		       (if quoted #'tramp-compat-file-name-quote #'identity)
6439		       (cadr (car (directory-files-and-attributes
6440				   file1 nil (regexp-quote elt1)))))
6441		      (file-remote-p (file-truename file2) 'localname)))
6442		    (delete-file file3)
6443		    (should-not (file-exists-p file3))))
6444
6445		(delete-file file2)
6446		(should-not (file-exists-p file2))
6447		(delete-directory file1)
6448		(should-not (file-exists-p file1))))
6449
6450	    ;; Check, that environment variables are set correctly.
6451            ;; We do not run on macOS due to encoding problems.  See
6452            ;; Bug#36940.
6453	    (when (and (tramp--test-expensive-test-p) (tramp--test-sh-p)
6454		       (not (tramp--test-crypt-p))
6455		       (not (eq system-type 'darwin)))
6456	      (dolist (elt files)
6457		(let ((envvar (concat "VAR_" (upcase (md5 elt))))
6458		      (elt (encode-coding-string elt coding-system-for-read))
6459		      (default-directory tramp-test-temporary-file-directory)
6460		      (process-environment process-environment))
6461		  (setenv envvar elt)
6462		  ;; The value of PS1 could confuse Tramp's detection
6463		  ;; of process output.  So we unset it temporarily.
6464		  (setenv "PS1")
6465		  (with-temp-buffer
6466		    (should (zerop (process-file "printenv" nil t nil)))
6467		    (goto-char (point-min))
6468		    (should
6469		     (re-search-forward
6470		      (format
6471		       "^%s=%s$"
6472		       (regexp-quote envvar)
6473		       (regexp-quote (getenv envvar))))))))))
6474
6475	;; Cleanup.
6476	(ignore-errors (kill-buffer buffer))
6477	(ignore-errors (delete-directory tmp-name1 'recursive))
6478	(ignore-errors (delete-directory tmp-name2 'recursive))))))
6479
6480(defun tramp--test-special-characters ()
6481  "Perform the test in `tramp-test41-special-characters*'."
6482  ;; Newlines, slashes and backslashes in file names are not
6483  ;; supported.  So we don't test.  And we don't test the tab
6484  ;; character on Windows or Cygwin, because the backslash is
6485  ;; interpreted as a path separator, preventing "\t" from being
6486  ;; expanded to <TAB>.
6487  (let ((files
6488	 (list
6489	  (cond ((or (tramp--test-ange-ftp-p)
6490		     (tramp--test-docker-p)
6491		     (tramp--test-gvfs-p)
6492		     (tramp--test-rclone-p)
6493		     (tramp--test-sudoedit-p)
6494		     (tramp--test-windows-nt-or-smb-p))
6495		 "foo bar baz")
6496		((or (tramp--test-adb-p)
6497		     (eq system-type 'cygwin))
6498		 " foo bar baz ")
6499		((tramp--test-sh-no-ls--dired-p)
6500		 "\tfoo bar baz\t")
6501		(t " foo\tbar baz\t"))
6502	  "@foo@bar@baz@"
6503	  (unless (tramp--test-windows-nt-and-out-of-band-p) "$foo$bar$$baz$")
6504	  "-foo-bar-baz-"
6505	  (unless (tramp--test-windows-nt-and-out-of-band-p) "%foo%bar%baz%")
6506	  "&foo&bar&baz&"
6507	  (unless (or (tramp--test-ftp-p)
6508		      (tramp--test-gvfs-p)
6509		      (tramp--test-windows-nt-or-smb-p))
6510	    "?foo?bar?baz?")
6511	  (unless (or (tramp--test-ftp-p)
6512		      (tramp--test-gvfs-p)
6513		      (tramp--test-windows-nt-or-smb-p))
6514	    "*foo*bar*baz*")
6515	  (if (or (tramp--test-gvfs-p) (tramp--test-windows-nt-or-smb-p))
6516	      "'foo'bar'baz'"
6517	    "'foo\"bar'baz\"")
6518	  "#foo~bar#baz~"
6519	  (unless (tramp--test-windows-nt-and-out-of-band-p)
6520	    (if (or (tramp--test-gvfs-p) (tramp--test-windows-nt-or-smb-p))
6521		"!foo!bar!baz!"
6522	      "!foo|bar!baz|"))
6523	  (if (or (tramp--test-gvfs-p)
6524		  (tramp--test-rclone-p)
6525		  (tramp--test-windows-nt-or-smb-p))
6526	      ";foo;bar;baz;"
6527	    ":foo;bar:baz;")
6528	  (unless (or (tramp--test-gvfs-p) (tramp--test-windows-nt-or-smb-p))
6529	    "<foo>bar<baz>")
6530	  "(foo)bar(baz)"
6531	  (unless (or (tramp--test-ftp-p) (tramp--test-gvfs-p)) "[foo]bar[baz]")
6532	  "{foo}bar{baz}")))
6533    ;; Simplify test in order to speed up.
6534    (apply #'tramp--test-check-files
6535	   (if (tramp--test-expensive-test-p)
6536	       files (list (mapconcat #'identity files ""))))))
6537
6538;; These tests are inspired by Bug#17238.
6539(ert-deftest tramp-test41-special-characters ()
6540  "Check special characters in file names."
6541  (skip-unless (not (getenv "EMACS_HYDRA_CI"))) ; SLOW ~ 245s
6542  (skip-unless (tramp--test-enabled))
6543  (skip-unless (not (tramp--test-rsync-p)))
6544  (skip-unless (not (tramp--test-rclone-p)))
6545
6546  (tramp--test-special-characters))
6547
6548(ert-deftest tramp-test41-special-characters-with-stat ()
6549  "Check special characters in file names.
6550Use the \"stat\" command."
6551  :tags '(:expensive-test)
6552  (skip-unless (not (getenv "EMACS_HYDRA_CI"))) ; SLOW ~ 287s
6553  (skip-unless (tramp--test-enabled))
6554  (skip-unless (tramp--test-sh-p))
6555  (skip-unless (not (tramp--test-rsync-p)))
6556  ;; We cannot use `tramp-test-vec', because this fails during compilation.
6557  (with-parsed-tramp-file-name tramp-test-temporary-file-directory nil
6558    (skip-unless (tramp-get-remote-stat v)))
6559
6560  (let ((tramp-connection-properties
6561	 (append
6562	  `((,(regexp-quote (file-remote-p tramp-test-temporary-file-directory))
6563	     "perl" nil))
6564	  tramp-connection-properties)))
6565    (tramp--test-special-characters)))
6566
6567(ert-deftest tramp-test41-special-characters-with-perl ()
6568  "Check special characters in file names.
6569Use the \"perl\" command."
6570  :tags '(:expensive-test)
6571  (skip-unless (not (getenv "EMACS_HYDRA_CI"))) ; SLOW ~ 266s
6572  (skip-unless (tramp--test-enabled))
6573  (skip-unless (tramp--test-sh-p))
6574  (skip-unless (not (tramp--test-rsync-p)))
6575  ;; We cannot use `tramp-test-vec', because this fails during compilation.
6576  (with-parsed-tramp-file-name tramp-test-temporary-file-directory nil
6577    (skip-unless (tramp-get-remote-perl v)))
6578
6579  (let ((tramp-connection-properties
6580	 (append
6581	  `((,(regexp-quote (file-remote-p tramp-test-temporary-file-directory))
6582	     "stat" nil)
6583	    ;; See `tramp-sh-handle-file-truename'.
6584	    (,(regexp-quote (file-remote-p tramp-test-temporary-file-directory))
6585	     "readlink" nil))
6586	  tramp-connection-properties)))
6587    (tramp--test-special-characters)))
6588
6589(ert-deftest tramp-test41-special-characters-with-ls ()
6590  "Check special characters in file names.
6591Use the \"ls\" command."
6592  :tags '(:expensive-test)
6593  (skip-unless (not (getenv "EMACS_HYDRA_CI"))) ; SLOW ~ 287s
6594  (skip-unless (tramp--test-enabled))
6595  (skip-unless (tramp--test-sh-p))
6596  (skip-unless (not (tramp--test-rsync-p)))
6597
6598  (let ((tramp-connection-properties
6599	 (append
6600	  `((,(regexp-quote (file-remote-p tramp-test-temporary-file-directory))
6601	     "perl" nil)
6602	    (,(regexp-quote (file-remote-p tramp-test-temporary-file-directory))
6603	     "stat" nil)
6604	    ;; See `tramp-sh-handle-file-truename'.
6605	    (,(regexp-quote (file-remote-p tramp-test-temporary-file-directory))
6606	     "readlink" nil))
6607	  tramp-connection-properties)))
6608    (tramp--test-special-characters)))
6609
6610(defun tramp--test-utf8 ()
6611  "Perform the test in `tramp-test42-utf8*'."
6612  (let* ((utf8 (if (and (eq system-type 'darwin)
6613			(memq 'utf-8-hfs (coding-system-list)))
6614		   'utf-8-hfs 'utf-8))
6615	 (coding-system-for-read utf8)
6616	 (coding-system-for-write utf8)
6617	 (file-name-coding-system
6618	  (coding-system-change-eol-conversion utf8 'unix)))
6619    (apply
6620     #'tramp--test-check-files
6621     (append
6622      (list
6623       (unless (tramp--test-hpux-p) "Γυρίστε το Γαλαξία με Ώτο Στοπ")
6624       (unless (tramp--test-hpux-p)
6625	 "أصبح بوسعك الآن تنزيل نسخة كاملة من موسوعة ويكيبيديا العربية لتصفحها بلا اتصال بالإنترنت")
6626       "银河系漫游指南系列"
6627       "Автостопом по гала́ктике"
6628       ;; Use codepoints without a name.  See Bug#31272.
6629       "™›šbung"
6630       ;; Use codepoints from Supplementary Multilingual Plane (U+10000
6631       ;; to U+1FFFF).
6632       "������")
6633
6634      (when (tramp--test-expensive-test-p)
6635	(delete-dups
6636	 (mapcar
6637	  ;; Use all available language specific snippets.
6638	  (lambda (x)
6639	    (and
6640             (stringp (setq x (eval (get-language-info (car x) 'sample-text) t)))
6641	     ;; Filter out strings which use unencodable characters.
6642	     (not (and (or (tramp--test-gvfs-p) (tramp--test-smb-p))
6643		       (unencodable-char-position
6644			0 (length x) file-name-coding-system nil x)))
6645	     ;; Filter out not displayable characters.
6646	     (setq x (mapconcat
6647		      (lambda (y)
6648			(and (char-displayable-p y) (char-to-string y)))
6649		      x ""))
6650             (not (string-empty-p x))
6651	     ;; ?\n and ?/ shouldn't be part of any file name.  ?\t,
6652	     ;; ?. and ?? do not work for "smb" method.  " " does not
6653	     ;; work at begin or end of the string for MS Windows.
6654	     (replace-regexp-in-string "[ \t\n/.?]" "" x)))
6655	  language-info-alist)))))))
6656
6657(ert-deftest tramp-test42-utf8 ()
6658  "Check UTF8 encoding in file names and file contents."
6659  (skip-unless (not (getenv "EMACS_HYDRA_CI"))) ; SLOW ~ 620s
6660  (skip-unless (tramp--test-enabled))
6661  (skip-unless (not (tramp--test-docker-p)))
6662  (skip-unless (not (tramp--test-rsync-p)))
6663  (skip-unless (not (tramp--test-windows-nt-and-out-of-band-p)))
6664  (skip-unless (not (tramp--test-ksh-p)))
6665  (skip-unless (not (tramp--test-gdrive-p)))
6666  (skip-unless (not (tramp--test-crypt-p)))
6667  (skip-unless (not (tramp--test-rclone-p)))
6668
6669  (tramp--test-utf8))
6670
6671(ert-deftest tramp-test42-utf8-with-stat ()
6672  "Check UTF8 encoding in file names and file contents.
6673Use the \"stat\" command."
6674  :tags '(:expensive-test)
6675  (skip-unless (not (getenv "EMACS_HYDRA_CI"))) ; SLOW ~ 595s
6676  (skip-unless (tramp--test-enabled))
6677  (skip-unless (tramp--test-sh-p))
6678  (skip-unless (not (tramp--test-docker-p)))
6679  (skip-unless (not (tramp--test-rsync-p)))
6680  (skip-unless (not (tramp--test-out-of-band-p))) ; SLOW
6681  (skip-unless (not (tramp--test-ksh-p)))
6682  (skip-unless (not (tramp--test-crypt-p)))
6683  ;; We cannot use `tramp-test-vec', because this fails during compilation.
6684  (with-parsed-tramp-file-name tramp-test-temporary-file-directory nil
6685    (skip-unless (tramp-get-remote-stat v)))
6686
6687  (let ((tramp-connection-properties
6688	 (append
6689	  `((,(regexp-quote (file-remote-p tramp-test-temporary-file-directory))
6690	     "perl" nil))
6691	  tramp-connection-properties)))
6692    (tramp--test-utf8)))
6693
6694(ert-deftest tramp-test42-utf8-with-perl ()
6695  "Check UTF8 encoding in file names and file contents.
6696Use the \"perl\" command."
6697  :tags '(:expensive-test)
6698  (skip-unless (not (getenv "EMACS_HYDRA_CI"))) ; SLOW ~ 620s
6699  (skip-unless (tramp--test-enabled))
6700  (skip-unless (tramp--test-sh-p))
6701  (skip-unless (not (tramp--test-docker-p)))
6702  (skip-unless (not (tramp--test-rsync-p)))
6703  (skip-unless (not (tramp--test-out-of-band-p))) ; SLOW
6704  (skip-unless (not (tramp--test-ksh-p)))
6705  (skip-unless (not (tramp--test-crypt-p)))
6706  ;; We cannot use `tramp-test-vec', because this fails during compilation.
6707  (with-parsed-tramp-file-name tramp-test-temporary-file-directory nil
6708    (skip-unless (tramp-get-remote-perl v)))
6709
6710  (let ((tramp-connection-properties
6711	 (append
6712	  `((,(regexp-quote (file-remote-p tramp-test-temporary-file-directory))
6713	     "stat" nil)
6714	    ;; See `tramp-sh-handle-file-truename'.
6715	    (,(regexp-quote (file-remote-p tramp-test-temporary-file-directory))
6716	     "readlink" nil))
6717	  tramp-connection-properties)))
6718    (tramp--test-utf8)))
6719
6720(ert-deftest tramp-test42-utf8-with-ls ()
6721  "Check UTF8 encoding in file names and file contents.
6722Use the \"ls\" command."
6723  :tags '(:expensive-test)
6724  (skip-unless (not (getenv "EMACS_HYDRA_CI"))) ; SLOW ~ 690s
6725  (skip-unless (tramp--test-enabled))
6726  (skip-unless (tramp--test-sh-p))
6727  (skip-unless (not (tramp--test-docker-p)))
6728  (skip-unless (not (tramp--test-rsync-p)))
6729  (skip-unless (not (tramp--test-out-of-band-p))) ; SLOW
6730  (skip-unless (not (tramp--test-ksh-p)))
6731  (skip-unless (not (tramp--test-crypt-p)))
6732
6733  (let ((tramp-connection-properties
6734	 (append
6735	  `((,(regexp-quote (file-remote-p tramp-test-temporary-file-directory))
6736	     "perl" nil)
6737	    (,(regexp-quote (file-remote-p tramp-test-temporary-file-directory))
6738	     "stat" nil)
6739	    ;; See `tramp-sh-handle-file-truename'.
6740	    (,(regexp-quote (file-remote-p tramp-test-temporary-file-directory))
6741	     "readlink" nil))
6742	  tramp-connection-properties)))
6743    (tramp--test-utf8)))
6744
6745(ert-deftest tramp-test43-file-system-info ()
6746  "Check that `file-system-info' returns proper values."
6747  (skip-unless (tramp--test-enabled))
6748  ;; Since Emacs 27.1.
6749  (skip-unless (fboundp 'file-system-info))
6750
6751  ;; `file-system-info' exists since Emacs 27.1.  We don't want to see
6752  ;; compiler warnings for older Emacsen.
6753  (let ((fsi (with-no-warnings
6754	       (file-system-info tramp-test-temporary-file-directory))))
6755    (skip-unless fsi)
6756    (should (and (consp fsi)
6757		 (= (length fsi) 3)
6758		 (numberp (nth 0 fsi))
6759		 (numberp (nth 1 fsi))
6760		 (numberp (nth 2 fsi))))))
6761
6762;; `tramp-test44-asynchronous-requests' could be blocked.  So we set a
6763;; timeout of 300 seconds, and we send a SIGUSR1 signal after 300
6764;; seconds.  Similar check is performed in the timer function.
6765(defconst tramp--test-asynchronous-requests-timeout 300
6766  "Timeout for `tramp-test44-asynchronous-requests'.")
6767
6768(defmacro tramp--test-with-proper-process-name-and-buffer (proc &rest body)
6769  "Set \"process-name\" and \"process-buffer\" connection properties.
6770The values are derived from PROC.  Run BODY.
6771This is needed in timer functions as well as process filters and sentinels."
6772  ;; FIXME: For tramp-sshfs.el, `processp' does not work.
6773  (declare (indent 1) (debug (processp body)))
6774  `(let* ((v (tramp-get-connection-property ,proc "vector" nil))
6775	  (pname (tramp-get-connection-property v "process-name" nil))
6776	  (pbuffer (tramp-get-connection-property v "process-buffer" nil)))
6777     (tramp--test-message
6778      "tramp--test-with-proper-process-name-and-buffer before %s %s"
6779      (tramp-get-connection-property v "process-name" nil)
6780      (tramp-get-connection-property v "process-buffer" nil))
6781     (if (process-name ,proc)
6782	 (tramp-set-connection-property v "process-name" (process-name ,proc))
6783       (tramp-flush-connection-property v "process-name"))
6784     (if (process-buffer ,proc)
6785	 (tramp-set-connection-property
6786	  v "process-buffer" (process-buffer ,proc))
6787       (tramp-flush-connection-property v "process-buffer"))
6788     (tramp--test-message
6789      "tramp--test-with-proper-process-name-and-buffer changed %s %s"
6790      (tramp-get-connection-property v "process-name" nil)
6791      (tramp-get-connection-property v "process-buffer" nil))
6792     (unwind-protect
6793	 (progn ,@body)
6794       (if pname
6795	   (tramp-set-connection-property v "process-name" pname)
6796	 (tramp-flush-connection-property v "process-name"))
6797       (if pbuffer
6798	   (tramp-set-connection-property v "process-buffer" pbuffer)
6799	 (tramp-flush-connection-property v "process-buffer")))))
6800
6801;; This test is inspired by Bug#16928.
6802(ert-deftest tramp-test44-asynchronous-requests ()
6803  "Check parallel asynchronous requests.
6804Such requests could arrive from timers, process filters and
6805process sentinels.  They shall not disturb each other."
6806  :tags (append '(:expensive-test :tramp-asynchronous-processes)
6807		(and (getenv "EMACS_HYDRA_CI") '(:unstable)))
6808  (skip-unless (tramp--test-enabled))
6809  (skip-unless (tramp--test-supports-processes-p))
6810  ;; Prior Emacs 27, `shell-file-name' was hard coded as "/bin/sh" for
6811  ;; remote processes in Emacs.  That doesn't work for tramp-adb.el.
6812  (when (tramp--test-adb-p)
6813    (skip-unless (tramp--test-emacs27-p)))
6814  (skip-unless (not (tramp--test-docker-p)))
6815  (skip-unless (not (tramp--test-telnet-p)))
6816  (skip-unless (not (tramp--test-sshfs-p)))
6817  (skip-unless (not (tramp--test-windows-nt-p)))
6818
6819  (with-timeout
6820      (tramp--test-asynchronous-requests-timeout (tramp--test-timeout-handler))
6821    (define-key special-event-map [sigusr1] #'tramp--test-timeout-handler)
6822    (let* (;; For the watchdog.
6823	   (default-directory (expand-file-name temporary-file-directory))
6824	   (shell-file-name (tramp--test-shell-file-name))
6825	   ;; It doesn't work on w32 systems.
6826	   (watchdog
6827            (start-process-shell-command
6828             "*watchdog*" nil
6829             (format
6830	      "sleep %d; kill -USR1 %d"
6831	      tramp--test-asynchronous-requests-timeout (emacs-pid))))
6832           (tmp-name (tramp--test-make-temp-name))
6833           (default-directory tmp-name)
6834           ;; Do not cache Tramp properties.
6835           (remote-file-name-inhibit-cache t)
6836           (process-file-side-effects t)
6837           ;; Suppress nasty messages.
6838           (inhibit-message t)
6839	   ;; Do not run delayed timers.
6840	   (timer-max-repeats 0)
6841	   ;; Number of asynchronous processes for test.  Tests on
6842	   ;; some machines handle less parallel processes.
6843           (number-proc
6844            (cond
6845             ((ignore-errors
6846               (string-to-number (getenv "REMOTE_PARALLEL_PROCESSES"))))
6847	     ((getenv "EMACS_HYDRA_CI") 5)
6848             (t 10)))
6849           ;; On hydra, timings are bad.
6850           (timer-repeat
6851            (cond
6852             ((getenv "EMACS_HYDRA_CI") 10)
6853             (t 1)))
6854	   ;; This is when all timers start.  We check inside the
6855	   ;; timer function, that we don't exceed timeout.
6856	   (timer-start (current-time))
6857           timer buffers kill-buffer-query-functions)
6858
6859      (unwind-protect
6860          (progn
6861            (make-directory tmp-name)
6862
6863            ;; Setup a timer in order to raise an ordinary command
6864            ;; again and again.  `vc-registered' is well suited,
6865            ;; because there are many checks.
6866            (setq
6867             timer
6868             (run-at-time
6869              0 timer-repeat
6870              (lambda ()
6871                (tramp--test-with-proper-process-name-and-buffer
6872                    (get-buffer-process (tramp-get-buffer tramp-test-vec))
6873                  (when (> (- (time-to-seconds) (time-to-seconds timer-start))
6874                           tramp--test-asynchronous-requests-timeout)
6875                    (tramp--test-timeout-handler))
6876                  (when buffers
6877                    (let ((time (float-time))
6878                          (default-directory tmp-name)
6879                          (file (buffer-name (seq-random-elt buffers)))
6880			  ;; A remote operation in a timer could
6881			  ;; confuse Tramp heavily.  So we ignore this
6882			  ;; error here.
6883			  (debug-ignored-errors
6884			   (cons 'remote-file-error debug-ignored-errors)))
6885                      (tramp--test-message
6886                       "Start timer %s %s" file (current-time-string))
6887		      (vc-registered file)
6888                      (tramp--test-message
6889                       "Stop timer %s %s" file (current-time-string))
6890                      ;; Adjust timer if it takes too much time.
6891                      (when (> (- (float-time) time) timer-repeat)
6892                        (setq timer-repeat (* 1.1 timer-repeat))
6893                        (setf (timer--repeat-delay timer) timer-repeat)
6894                        (tramp--test-message
6895                         "Increase timer %s" timer-repeat))))))))
6896
6897            ;; Create temporary buffers.  The number of buffers
6898            ;; corresponds to the number of processes; it could be
6899            ;; increased in order to make pressure on Tramp.
6900            (dotimes (_ number-proc)
6901              (setq buffers (cons (generate-new-buffer "foo") buffers)))
6902
6903            ;; Open asynchronous processes.  Set process filter and sentinel.
6904            (dolist (buf buffers)
6905	      ;; Activate timer.
6906	      (sit-for 0.01 'nodisp)
6907              (let ((proc
6908                     (start-file-process-shell-command
6909                      (buffer-name buf) buf
6910                      (concat
6911		       "(read line && echo $line >$line && echo $line);"
6912		       "(read line && cat $line);"
6913		       "(read line && rm -f $line)")))
6914                    (file (expand-file-name (buffer-name buf))))
6915                ;; Remember the file name.  Add counter.
6916                (process-put proc 'foo file)
6917                (process-put proc 'bar 0)
6918                ;; Add process filter.
6919                (set-process-filter
6920                 proc
6921                 (lambda (proc string)
6922		   (tramp--test-with-proper-process-name-and-buffer proc
6923                     (tramp--test-message
6924                      "Process filter %s %s %s"
6925		      proc string (current-time-string))
6926                     (with-current-buffer (process-buffer proc)
6927                       (insert string))
6928                     (when (< (process-get proc 'bar) 2)
6929		       (dired-uncache (process-get proc 'foo))
6930                       (should (file-attributes (process-get proc 'foo)))))))
6931                ;; Add process sentinel.  It shall not perform remote
6932                ;; operations, triggering Tramp processes.  This blocks.
6933                (set-process-sentinel
6934                 proc
6935                 (lambda (proc _state)
6936		   (tramp--test-with-proper-process-name-and-buffer proc
6937                     (tramp--test-message
6938                      "Process sentinel %s %s" proc (current-time-string)))))))
6939
6940            ;; Send a string to the processes.  Use a random order of
6941            ;; the buffers.  Mix with regular operation.
6942            (let ((buffers (copy-sequence buffers)))
6943              (while buffers
6944                (let* ((buf (seq-random-elt buffers))
6945                       (proc (get-buffer-process buf))
6946                       (file (process-get proc 'foo))
6947                       (count (process-get proc 'bar)))
6948                  (tramp--test-message
6949                   "Start action %d %s %s" count buf (current-time-string))
6950                  ;; Regular operation prior process action.
6951		  (dired-uncache file)
6952                  (if (= count 0)
6953                      (should-not (file-attributes file))
6954                    (should (file-attributes file)))
6955                  ;; Send string to process.
6956                  (process-send-string proc (format "%s\n" (buffer-name buf)))
6957                  (while (accept-process-output nil 0))
6958                  (tramp--test-message
6959                   "Continue action %d %s %s" count buf (current-time-string))
6960                  ;; Regular operation post process action.
6961		  (dired-uncache file)
6962                  (if (= count 2)
6963                      (should-not (file-attributes file))
6964                    (should (file-attributes file)))
6965                  (tramp--test-message
6966                   "Stop action %d %s %s" count buf (current-time-string))
6967                  (process-put proc 'bar (1+ count))
6968                  (unless (process-live-p proc)
6969                    (setq buffers (delq buf buffers))))))
6970
6971            ;; Checks.  All process output shall exists in the
6972            ;; respective buffers.  All created files shall be
6973            ;; deleted.
6974            (tramp--test-message "Check %s" (current-time-string))
6975            (dolist (buf buffers)
6976              (with-current-buffer buf
6977                (should
6978		 (string-equal
6979		  ;; tramp-adb.el echoes, so we must add the three strings.
6980		  (if (tramp--test-adb-p)
6981		      (format "%s\n%s\n%s\n%s\n%s\n" buf buf buf buf buf)
6982		    (format "%s\n%s\n" buf buf))
6983		  (buffer-string)))))
6984            (should-not
6985             (directory-files
6986              tmp-name nil directory-files-no-dot-files-regexp)))
6987
6988        ;; Cleanup.
6989        (define-key special-event-map [sigusr1] #'ignore)
6990        (ignore-errors (quit-process watchdog))
6991        (dolist (buf buffers)
6992          (ignore-errors (delete-process (get-buffer-process buf)))
6993          (ignore-errors (kill-buffer buf)))
6994        (ignore-errors (cancel-timer timer))
6995        (ignore-errors (delete-directory tmp-name 'recursive))))))
6996
6997;; (tramp--test--deftest-direct-async-process tramp-test44-asynchronous-requests
6998;;   "Check parallel direct asynchronous requests." 'unstable)
6999
7000(ert-deftest tramp-test45-dired-compress-file ()
7001  "Check that Tramp (un)compresses normal files."
7002  (skip-unless (tramp--test-enabled))
7003  (skip-unless (tramp--test-sh-p))
7004  (skip-unless (not (tramp--test-crypt-p)))
7005  ;; Starting with Emacs 29.1, `dired-compress-file' is performed by
7006  ;; default handler.
7007  (skip-unless (not (tramp--test-emacs29-p)))
7008
7009  (let ((default-directory tramp-test-temporary-file-directory)
7010        (tmp-name (tramp--test-make-temp-name)))
7011    (write-region "foo" nil tmp-name)
7012    (dired default-directory)
7013    (dired-revert)
7014    (dired-goto-file tmp-name)
7015    (should-not (dired-compress))
7016    (should (string= (concat tmp-name ".gz") (dired-get-filename)))
7017    (should-not (dired-compress))
7018    (should (string= tmp-name (dired-get-filename)))
7019    (delete-file tmp-name)))
7020
7021(ert-deftest tramp-test45-dired-compress-dir ()
7022  "Check that Tramp (un)compresses directories."
7023  (skip-unless (tramp--test-enabled))
7024  (skip-unless (tramp--test-sh-p))
7025  (skip-unless (not (tramp--test-crypt-p)))
7026  ;; Starting with Emacs 29.1, `dired-compress-file' is performed by
7027  ;; default handler.
7028  (skip-unless (not (tramp--test-emacs29-p)))
7029
7030  (let ((default-directory tramp-test-temporary-file-directory)
7031        (tmp-name (tramp--test-make-temp-name)))
7032    (make-directory tmp-name)
7033    (dired default-directory)
7034    (dired-revert)
7035    (dired-goto-file tmp-name)
7036    (should-not (dired-compress))
7037    (should (string= (concat tmp-name ".tar.gz") (dired-get-filename)))
7038    (should-not (dired-compress))
7039    (should (string= tmp-name (dired-get-filename)))
7040    (delete-directory tmp-name)
7041    (delete-file (concat tmp-name ".tar.gz"))))
7042
7043;; This test is inspired by Bug#29163.
7044(ert-deftest tramp-test46-auto-load ()
7045  "Check that Tramp autoloads properly."
7046  ;; If we use another syntax but `default', Tramp is already loaded
7047  ;; due to the `tramp-change-syntax' call.
7048  (skip-unless (eq tramp-syntax 'default))
7049  (skip-unless (tramp--test-enabled))
7050
7051  (let ((default-directory (expand-file-name temporary-file-directory))
7052	(code
7053	 (format
7054	  ;; Suppress method name check.
7055	  "(let ((non-essential t)) \
7056             (message \"Tramp loaded: %%s\" (and (file-remote-p %S) t)))"
7057	  tramp-test-temporary-file-directory)))
7058    (should
7059     (string-match-p
7060      "Tramp loaded: t[\n\r]+"
7061      (shell-command-to-string
7062       (format
7063	"%s -batch -Q -L %s --eval %s"
7064	(shell-quote-argument
7065	 (expand-file-name invocation-name invocation-directory))
7066	(mapconcat #'shell-quote-argument load-path " -L ")
7067	(shell-quote-argument code)))))))
7068
7069(ert-deftest tramp-test46-delay-load ()
7070  "Check that Tramp is loaded lazily, only when needed."
7071  ;; Tramp is neither loaded at Emacs startup, nor when completing a
7072  ;; non-Tramp file name like "/foo".  Completing a Tramp-alike file
7073  ;; name like "/foo:" autoloads Tramp, when `tramp-mode' is t.
7074  (let ((default-directory (expand-file-name temporary-file-directory))
7075	(code
7076	 "(progn \
7077           (setq tramp-mode %s) \
7078	   (message \"Tramp loaded: %%s\" (featurep 'tramp)) \
7079	   (file-name-all-completions \"/foo\" \"/\") \
7080	   (message \"Tramp loaded: %%s\" (featurep 'tramp)) \
7081	   (file-name-all-completions \"/foo:\" \"/\") \
7082	   (message \"Tramp loaded: %%s\" (featurep 'tramp)))"))
7083    ;; Tramp doesn't load when `tramp-mode' is nil.
7084    (dolist (tm '(t nil))
7085      (should
7086       (string-match-p
7087	(format
7088       "Tramp loaded: nil[\n\r]+Tramp loaded: nil[\n\r]+Tramp loaded: %s[\n\r]+"
7089	 tm)
7090	(shell-command-to-string
7091	 (format
7092	  "%s -batch -Q -L %s --eval %s"
7093	  (shell-quote-argument
7094	   (expand-file-name invocation-name invocation-directory))
7095	  (mapconcat #'shell-quote-argument load-path " -L ")
7096	  (shell-quote-argument (format code tm)))))))))
7097
7098(ert-deftest tramp-test46-recursive-load ()
7099  "Check that Tramp does not fail due to recursive load."
7100  (skip-unless (tramp--test-enabled))
7101
7102  (let ((default-directory (expand-file-name temporary-file-directory)))
7103    (dolist (code
7104	     (list
7105	      (format
7106	       "(expand-file-name %S)" tramp-test-temporary-file-directory)
7107	      (format
7108	       "(let ((default-directory %S)) (expand-file-name %S))"
7109	       tramp-test-temporary-file-directory
7110	       temporary-file-directory)))
7111      (should-not
7112       (string-match-p
7113	"Recursive load"
7114	(shell-command-to-string
7115	 (format
7116	  "%s -batch -Q -L %s --eval %s"
7117	  (shell-quote-argument
7118	   (expand-file-name invocation-name invocation-directory))
7119	  (mapconcat #'shell-quote-argument load-path " -L ")
7120	  (shell-quote-argument code))))))))
7121
7122(ert-deftest tramp-test46-remote-load-path ()
7123  "Check that Tramp autoloads its packages with remote `load-path'."
7124  ;; `tramp-cleanup-all-connections' is autoloaded from tramp-cmds.el.
7125  ;; It shall still work, when a remote file name is in the
7126  ;; `load-path'.
7127  (let ((default-directory (expand-file-name temporary-file-directory))
7128	(code
7129	 "(let ((force-load-messages t) \
7130		(load-path (cons \"/foo:bar:\" load-path))) \
7131	    (tramp-cleanup-all-connections))"))
7132    (should
7133     (string-match-p
7134      (format
7135       "Loading %s"
7136       (regexp-quote
7137        (expand-file-name
7138         "tramp-cmds" (file-name-directory (locate-library "tramp")))))
7139      (shell-command-to-string
7140       (format
7141	"%s -batch -Q -L %s -l tramp-sh --eval %s"
7142	(shell-quote-argument
7143	 (expand-file-name invocation-name invocation-directory))
7144	(mapconcat #'shell-quote-argument load-path " -L ")
7145	(shell-quote-argument code)))))))
7146
7147(ert-deftest tramp-test47-unload ()
7148  "Check that Tramp and its subpackages unload completely.
7149Since it unloads Tramp, it shall be the last test to run."
7150  :tags '(:expensive-test)
7151  (skip-unless noninteractive)
7152  ;; We have autoloaded objects from tramp.el and tramp-archive.el.
7153  ;; In order to remove them, we first need to load both packages.
7154  (require 'tramp)
7155  (require 'tramp-archive)
7156  (should (featurep 'tramp))
7157  (should (featurep 'tramp-archive))
7158  ;; This unloads also tramp-archive.el and tramp-theme.el if needed.
7159  (unload-feature 'tramp 'force)
7160  ;; No Tramp feature must be left.
7161  (should-not (featurep 'tramp))
7162  (should-not (featurep 'tramp-archive))
7163  (should-not (featurep 'tramp-theme))
7164  (should-not
7165   (all-completions
7166    "tramp" (delq 'tramp-tests (delq 'tramp-archive-tests features))))
7167  ;; `file-name-handler-alist' must be clean.
7168  (should-not (all-completions "tramp" (mapcar #'cdr file-name-handler-alist)))
7169  ;; There shouldn't be left a bound symbol, except buffer-local
7170  ;; variables, and autoload functions.  We do not regard our test
7171  ;; symbols, and the Tramp unload hooks.
7172  (mapatoms
7173   (lambda (x)
7174     (and (or (and (boundp x) (null (local-variable-if-set-p x)))
7175	      (and (functionp x) (null (autoloadp (symbol-function x)))))
7176	  (string-match-p "^tramp" (symbol-name x))
7177	  ;; `tramp-completion-mode' is autoloaded in Emacs < 28.1.
7178	  (not (eq 'tramp-completion-mode x))
7179	  (not (string-match-p "^tramp\\(-archive\\)?--?test" (symbol-name x)))
7180	  (not (string-match-p "unload-hook$" (symbol-name x)))
7181	  (ert-fail (format "`%s' still bound" x)))))
7182  ;; The defstruct `tramp-file-name' and all its internal functions
7183  ;; shall be purged.
7184  (should-not (cl--find-class 'tramp-file-name))
7185  (mapatoms
7186   (lambda (x)
7187     (and (functionp x)
7188          (string-match-p "tramp-file-name" (symbol-name x))
7189          (ert-fail (format "Structure function `%s' still exists" x)))))
7190  ;; There shouldn't be left a hook function containing a Tramp
7191  ;; function.  We do not regard the Tramp unload hooks.
7192  (mapatoms
7193   (lambda (x)
7194     (and (boundp x)
7195	  (string-match-p "-\\(hook\\|function\\)s?$" (symbol-name x))
7196	  (not (string-match-p "unload-hook$" (symbol-name x)))
7197	  (consp (symbol-value x))
7198	  (ignore-errors (all-completions "tramp" (symbol-value x)))
7199	  (ert-fail (format "Hook `%s' still contains Tramp function" x))))))
7200
7201(defun tramp-test-all (&optional interactive)
7202  "Run all tests for \\[tramp].
7203If INTERACTIVE is non-nil, the tests are run interactively."
7204  (interactive "p")
7205  (funcall
7206   (if interactive #'ert-run-tests-interactively #'ert-run-tests-batch)
7207   "^tramp"))
7208
7209;; TODO:
7210
7211;; * dired-uncache (partly done in other test functions)
7212;; * file-equal-p (partly done in `tramp-test21-file-links')
7213;; * file-in-directory-p
7214;; * file-name-case-insensitive-p
7215;; * tramp-get-remote-gid
7216;; * tramp-get-remote-uid
7217;; * tramp-set-file-uid-gid
7218
7219;; * Work on skipped tests.  Make a comment, when it is impossible.
7220;; * Revisit expensive tests, once problems in `tramp-error' are solved.
7221;; * Fix `tramp-test06-directory-file-name' for "ftp".
7222;; * Implement `tramp-test31-interrupt-process' for "adb", "sshfs" and
7223;;   for direct async processes.
7224;; * Check, why direct async processes do not work for
7225;;   `tramp-test44-asynchronous-requests'.
7226
7227(provide 'tramp-tests)
7228
7229;;; tramp-tests.el ends here
7230