emacs/test/src/fileio-tests.el
Sean Whitton fdab8a9185 ; Revert Eric's commits from February.
These will be resubmitted as patches for review.

Revert "Repair another test bollixed by aggressive optimization."
This reverts commit 47735e0243.

Revert "Repair ab ecal test by making a variable kexical,"
This reverts commit ca42055b0c.

Revert "Complete the test set for floatfns,c."
This reverts commit 1b0c8d6b95.

Revert "Tesrts for the portable primitives in fileio.c."
This reverts commit a339c6827c.

Revert "Tests for primitives in coding.c and charset.c."
This reverts commit 5749b2e4f4.

Revert "Tests for primitives from the character.c module."
This reverts commit b09f8df206.

Revert "Tests for the lreaf.c amd print.c primitives."
This reverts commit d7a3d442b4.

Revert "Tests for remaining functions iun eval.c."
This reverts commit cd038e5617.

Revert "Completing test coverage for dataa.c orimitives."
This reverts commit a6e19d6179.

Revert "More correctness tesrs for orinitives from fns.c."
This reverts commit 40ff4512ad.

Revert "More tests for edit functions, buffers, and markers."
This reverts commit 67e8f87562.

Revert "Added more buffer/marker/editing test coverage."
This reverts commit 3dda4b85e8.

Revert "Category/charset/coding + char-table tests."
This reverts commit 7a93a7b334.

Revert "More test coverage improvements."
This reverts commit fc7339c46d.

Revert "More test coverage improvements."
This reverts commit 95329bf445.

Revert "More test coverage improvements for ERT."
This reverts commit e42c579a54.

Revert "Crrections to tedt coverrage extensuion after bootstrap build."
This reverts commit 90af3295c7.

Revert "Improve test coverage of builtin predicates."
This reverts commit 6eb170b007.

Revert "Tests for 2 marker primitives previously not covered."
This reverts commit 6d7f0acf9c.

Revert "Tests for 7 editor primitives previously not covered."
This reverts commit bb403e70ae.
2026-05-07 15:29:53 +01:00

281 lines
12 KiB
EmacsLisp

;;; fileio-tests.el --- unit tests for src/fileio.c -*- lexical-binding: t; -*-
;; Copyright 2017-2026 Free Software Foundation, Inc.
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Code:
(require 'ert)
(require 'ert-x)
(defun try-link (target link)
(make-symbolic-link target link)
(let* ((read-link (file-symlink-p link))
(failure (unless (string-equal target read-link)
(list 'string-equal target read-link))))
(delete-file link)
failure))
(defun fileio-tests--symlink-failure ()
(ert-with-temp-directory dir
(let* ((link (expand-file-name "link" dir)))
(let (failure
(char 0))
(while (and (not failure) (< char 127))
(setq char (1+ char))
(when (and (eq system-type 'cygwin) (eq char 92))
(setq char (1+ char)))
(setq failure (try-link (string char) link)))
(or failure
(try-link "/:" link))))))
(ert-deftest fileio-tests--odd-symlink-chars ()
"Check that any non-NULL ASCII character can appear in a symlink.
Also check that an encoding error can appear in a symlink."
;; Some Windows versions don't support symlinks, and those which do
;; will pop up UAC elevation prompts, so we disable this test on
;; MS-Windows.
(skip-when (eq system-type 'windows-nt))
(should (equal nil (fileio-tests--symlink-failure))))
(ert-deftest fileio-tests--directory-file-name ()
(should (equal (directory-file-name "/") "/"))
(should (equal (directory-file-name "//") "//"))
(should (equal (directory-file-name "///") "/"))
(should (equal (directory-file-name "////") "/"))
(should (equal (directory-file-name "/abc") "/abc"))
(should (equal (directory-file-name "/abc/") "/abc"))
(should (equal (directory-file-name "/abc//") "/abc")))
(ert-deftest fileio-tests--directory-file-name-dos-nt ()
"Like fileio-tests--directory-file-name, but for DOS_NT systems."
(skip-unless (memq system-type '(ms-dos windows-nt)))
(should (equal (directory-file-name "d:/") "d:/"))
(should (equal (directory-file-name "d://") "d:/"))
(should (equal (directory-file-name "d:///") "d:/"))
(should (equal (directory-file-name "d:////") "d:/"))
(should (equal (directory-file-name "d:/abc") "d:/abc"))
(should (equal (directory-file-name "d:/abc/") "d:/abc"))
(should (equal (directory-file-name "d:/abc//") "d:/abc")))
(ert-deftest fileio-tests--file-name-as-directory ()
(should (equal (file-name-as-directory "") "./"))
(should (equal (file-name-as-directory "/") "/"))
(should (equal (file-name-as-directory "//") "//"))
(should (equal (file-name-as-directory "///") "///"))
(should (equal (file-name-as-directory "////") "////"))
(should (equal (file-name-as-directory "/abc") "/abc/"))
(should (equal (file-name-as-directory "/abc/") "/abc/"))
(should (equal (file-name-as-directory "/abc//") "/abc//")))
(ert-deftest fileio-tests--file-name-as-directory-dos-nt ()
"Like fileio-tests--file-name-as-directory, but for DOS_NT systems."
(skip-unless (memq system-type '(ms-dos windows-nt)))
(should (equal (file-name-as-directory "d:/") "d:/"))
(should (equal (file-name-as-directory "d:\\") "d:/"))
(should (equal (file-name-as-directory "d://") "d://"))
(should (equal (file-name-as-directory "d:///") "d:///"))
(should (equal (file-name-as-directory "d:////") "d:////"))
(should (equal (file-name-as-directory "d:\\\\\\\\") "d:////"))
(should (equal (file-name-as-directory "d:/abc") "d:/abc/"))
(should (equal (file-name-as-directory "D:\\abc") "d:/abc/"))
(should (equal (file-name-as-directory "d:/abc/") "d:/abc/"))
(should (equal (file-name-as-directory "D:\\abc/") "d:/abc/"))
(should (equal (file-name-as-directory "D:/abc//") "d:/abc//")))
(ert-deftest fileio-tests--relative-HOME ()
"Test that `expand-file-name' works even when HOME is relative."
(with-environment-variables (("HOME" "a/b/c"))
(should (equal (expand-file-name "~/foo")
(expand-file-name "a/b/c/foo")))
(when (memq system-type '(ms-dos windows-nt))
;; Test expansion of drive-relative file names.
(setenv "HOME" "x:foo")
(should (equal (expand-file-name "~/bar") "x:/foo/bar")))))
(ert-deftest fileio-tests--insert-file-interrupt ()
(ert-with-temp-file f
(let ((text "-*- coding: binary -*-\n\xc3\xc3help"))
(write-region text nil f nil 'silent)
(with-temp-buffer
(catch 'toto
(let ((set-auto-coding-function (lambda (&rest _) (throw 'toto nil))))
(insert-file-contents f)))
(goto-char (point-min))
(unless (eobp)
(forward-line 1)
(let ((c1 (char-after)))
(forward-char 1)
(should (equal c1 (char-before)))
(should (equal c1 (char-after)))))))))
(ert-deftest fileio-tests--relative-default-directory ()
"Test `expand-file-name' when `default-directory' is relative."
(let ((default-directory "some/relative/name"))
(should (file-name-absolute-p (expand-file-name "foo"))))
(let* ((default-directory "~foo")
(name (expand-file-name "bar")))
(should (and (file-name-absolute-p name)
(not (eq (aref name 0) ?~))))))
(ert-deftest fileio-tests--expand-file-name-null-bytes ()
"Test that `expand-file-name' checks for null bytes in filenames."
(should-error (expand-file-name (concat "file" (char-to-string ?\0) ".txt"))
:type 'wrong-type-argument)
(should-error (expand-file-name "file.txt" (concat "dir" (char-to-string ?\0)))
:type 'wrong-type-argument)
(let ((default-directory (concat "dir" (char-to-string ?\0))))
(should-error (expand-file-name "file.txt") :type 'wrong-type-argument)))
(ert-deftest fileio-tests--file-name-absolute-p ()
"Test `file-name-absolute-p'."
(dolist (suffix '("" "/" "//" "/foo" "/foo/" "/foo//" "/foo/bar"))
(unless (string-equal suffix "")
(should (file-name-absolute-p suffix)))
(should (file-name-absolute-p (concat "~" suffix)))
(when (user-full-name user-login-name)
(should (file-name-absolute-p (concat "~" user-login-name suffix))))
(unless (user-full-name "nosuchuser")
(should (not (file-name-absolute-p (concat "~nosuchuser" suffix)))))))
(ert-deftest fileio-tests--circular-after-insert-file-functions ()
"Test `after-insert-file-functions' as a circular list."
(ert-with-temp-file f
:suffix "fileio"
(let ((after-insert-file-functions (list 'identity)))
(setcdr after-insert-file-functions after-insert-file-functions)
(write-region "hello\n" nil f nil 'silent)
(should-error (insert-file-contents f) :type 'circular-list))))
(ert-deftest fileio-tests/null-character ()
(should-error (file-exists-p "/foo\0bar")
:type 'wrong-type-argument))
(ert-deftest fileio-tests/file-name-concat ()
(should (equal (file-name-concat "foo" "bar") "foo/bar"))
(should (equal (file-name-concat "foo" "bar") "foo/bar"))
(should (equal (file-name-concat "foo" "bar" "zot") "foo/bar/zot"))
(should (equal (file-name-concat "foo/" "bar") "foo/bar"))
(should (equal (file-name-concat "foo//" "bar") "foo//bar"))
(should (equal (file-name-concat "foo/" "bar/" "zot") "foo/bar/zot"))
(should (equal (file-name-concat "fóo" "bar") "fóo/bar"))
(should (equal (file-name-concat "foo" "bár") "foo/bár"))
(should (equal (file-name-concat "fóo" "bár") "fóo/bár"))
(let ((string (make-string 5 ?a)))
(should (not (multibyte-string-p string)))
(aset string 2 255)
(should (not (multibyte-string-p string)))
(should (equal (file-name-concat "fóo" string) "fóo/aa\377aa")))
(should (equal (file-name-concat "foo") "foo"))
(should (equal (file-name-concat "foo/") "foo/"))
(should (equal (file-name-concat "foo" "") "foo"))
(should (equal (file-name-concat "foo" "" "" "" nil) "foo"))
(should (equal (file-name-concat "" "bar") "bar"))
(should (equal (file-name-concat "" "") "")))
(ert-deftest fileio-tests--non-regular-insert ()
(skip-unless (file-exists-p "/dev/urandom"))
(with-temp-buffer
(set-buffer-multibyte nil)
(should-error (insert-file-contents "/dev/urandom" nil 5 10))
(insert-file-contents "/dev/urandom" nil nil 10)
(should (= (buffer-size) 10))))
(ert-deftest fileio-tests--read-directory ()
"Make sure insertring a directory fails with a platform-independent error."
(ert-with-temp-directory dir
(let* ((dir-name (directory-file-name dir))
(err (should-error (insert-file-contents dir-name)))
(desc-string
;; On MS-Windows we fail trying to 'open' a directory.
(if (eq system-type 'windows-nt)
"Opening input file"
"Read error")))
(should (equal err
(list 'file-error
desc-string
"Is a directory"
dir-name))))))
(defun fileio-tests--identity-expand-handler (_ file &rest _)
file)
(put 'fileio-tests--identity-expand-handler 'operations '(expand-file-name))
(ert-deftest fileio--file-name-case-insensitive-p ()
;; Check that we at least don't crash if given nonexistent files
;; without a directory (bug#56443).
;; Use an identity file-name handler, as if called by `ffap'.
(let* ((file-name-handler-alist
'(("^mailto:" . fileio-tests--identity-expand-handler)))
(file "mailto:snowball@hell.com"))
;; Check that `expand-file-name' is identity for this name.
(should (equal (expand-file-name file nil) file))
(file-name-case-insensitive-p file)))
(ert-deftest fileio-tests-invalid-UNC ()
(skip-unless (eq system-type 'windows-nt))
;; These should not crash, see bug#70914.
(should-not (file-exists-p "//"))
(should (file-attributes "//")))
(ert-deftest fileio-tests-w32-time-stamp-granularity ()
"Test 100-nsec granularity of file time stamps on MS-Windows."
(skip-unless (eq system-type 'windows-nt))
;; FIXME: This only works on NTFS volumes, so should skip the test if
;; not NTFS. But we don't expose the filesystem type to Lisp.
(let ((tfile (make-temp-file "tstamp")))
(unwind-protect
(progn
(set-file-times tfile (encode-time '(59.123456789 15 23 01 02 2025)))
(should
(equal (format-time-string "%Y/%m/%d %H:%M:%S.%N"
(file-attribute-modification-time
(file-attributes tfile)))
;; Last 2 digits of seconds must be zero due to
;; 100-nsec resolution of Windows file time stamps.
"2025/02/01 23:15:59.123456700")))
(delete-file tfile))))
(defconst ert--tests-dir
(file-name-directory (macroexp-file-name)))
(ert-deftest fileio-tests--insert-file-contents-supersession ()
(ert-with-temp-file file
(write-region "foo" nil file)
(let* ((asked nil)
(buf (find-file-noselect file))
(auast (lambda (&rest _) (setq asked t))))
(unwind-protect
(with-current-buffer buf
;; Pretend someone else edited the file.
(write-region "bar" nil file 'append)
;; Use `advice-add' rather than `cl-letf' because the function
;; may not be defined yet.
(advice-add 'ask-user-about-supersession-threat :override auast)
;; Modify the local buffer via `insert-file-contents'.
(insert-file-contents
(expand-file-name "lread-resources/somelib.el"
ert--tests-dir)
nil nil nil 'replace))
(advice-remove 'ask-user-about-supersession-threat auast)
(kill-buffer buf))
;; We should have prompted about the supersession threat.
(should asked))))
;;; fileio-tests.el ends here