From unknown Mon Aug 18 14:26:07 2025 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-Mailer: MIME-tools 5.509 (Entity 5.509) Content-Type: text/plain; charset=utf-8 From: bug#79177 <79177@debbugs.gnu.org> To: bug#79177 <79177@debbugs.gnu.org> Subject: Status: 31.0.50; [PATCH] [v4] Add tests to ispell.el Reply-To: bug#79177 <79177@debbugs.gnu.org> Date: Mon, 18 Aug 2025 21:26:07 +0000 retitle 79177 31.0.50; [PATCH] [v4] Add tests to ispell.el reassign 79177 emacs submitter 79177 Lockywolf severity 79177 normal tag 79177 patch thanks From debbugs-submit-bounces@debbugs.gnu.org Tue Aug 05 10:09:08 2025 Received: (at submit) by debbugs.gnu.org; 5 Aug 2025 14:09:08 +0000 Received: from localhost ([127.0.0.1]:56596 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ujILi-0002sg-FB for submit@debbugs.gnu.org; Tue, 05 Aug 2025 10:09:08 -0400 Received: from lists.gnu.org ([2001:470:142::17]:49948) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1ujH5k-0006kt-7h for submit@debbugs.gnu.org; Tue, 05 Aug 2025 08:48:34 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ujH5W-00043m-AL for bug-gnu-emacs@gnu.org; Tue, 05 Aug 2025 08:48:19 -0400 Received: from coconut.lockywolf.net ([213.165.252.157]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ujH5R-0008Uk-BA for bug-gnu-emacs@gnu.org; Tue, 05 Aug 2025 08:48:17 -0400 Received: from laptop.lockywolf.net (unknown [IPv6:2001:470:24:315::102]) by coconut.lockywolf.net (Postfix) with ESMTPSA id 25EA53863F for ; Tue, 5 Aug 2025 20:48:07 +0800 (CST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=lockywolf.net; s=2024-10-06; t=1754398090; bh=BGmH/UPHLrNSbBil7F6llHn8LW5qL4sQSp2dq5n1nzE=; h=From:To:Subject:Date; b=XbRLIKEtAQfxW41aVbppsRtKNM3RhcKwBQfYvbBoFCRSt77Kkp7MRVBxGSLUdkSPQ zLbeuaHQMUgCh97dcYJf2ffeNOHYpgtosD03GE1YkJ2h6gK/vhn5PrmBadS1wqwu/b q2Eh/eID9xbKLq8ubZkhg4mLgg3R4TwLUmQOiuyML37xc2s8P1kUgnBq6Dldg/tNsq 6UltPps6v0LNSGGxSAItQ7BFzY4FmtzrNKcxUhxQ6A7P+vhj5/Xenbb1DXnH+Q7vtY 4uit+PX2u6XWuRiRIIeJtFQGWI2vfYILQ6kF9Z1Qf88N5gJazfufNe9n60eQVY9mv1 YSKTiRtAoiWMA== From: Lockywolf To: bug-gnu-emacs@gnu.org Subject: 31.0.50; [PATCH] [v4] Add tests to ispell.el User-Agent: mu4e 1.12.9; emacs 31.0.50 X-Debbugs-Cc: Date: Tue, 05 Aug 2025 20:47:57 +0800 Message-ID: <87cy9aj5oi.fsf@laptop.lockywolf.net> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Received-SPF: pass client-ip=213.165.252.157; envelope-from=for_emacs_1@lockywolf.net; helo=coconut.lockywolf.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Spam-Score: 0.9 (/) X-Debbugs-Envelope-To: submit X-Mailman-Approved-At: Tue, 05 Aug 2025 10:09:02 -0400 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -0.1 (/) --=-=-= Content-Type: multipart/signed; boundary="==-=-="; micalg=pgp-sha256; protocol="application/pgp-signature" --==-=-= Content-Type: text/plain Content-Transfer-Encoding: quoted-printable Continuing the discussion from emacs-devel: https://lists.gnu.org/archive/html/emacs-devel/2025-07/msg00514.html Attached is the patch which adds 36 tests for ispell.el. The following symbols are tested: 0. ispell-program-name 1. ispell-with-safe-default-directory 2. ispell-call-process 3. ispell-create-debug-buffer 4. ispell-valid-dictionary-list 5. ispell-add-per-file-word-list 6. ispell-buffer-local-words 7. ispell-init-process 8. ispell-buffer-local-dict =2D-=20 Your sincerely, Vladimir Nikishkin (MiEr, lockywolf) (Laptop) --==-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iHUEARYIAB0WIQSWBebxCAoqaJP8N/j71sGyD+xtqgUCaJH9gQAKCRD71sGyD+xt qlSPAQDFsA/opuzhPBGzy03/66AiPUZCwoLiK4rAG7TymoWZZQD9FpyB0DI6PBWq 5ut34wsBe6rwO2eUEVFnydCqjVvH3Aw= =M8Kd -----END PGP SIGNATURE----- --==-=-=-- --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-ispell.el-Add-36-tests.patch >From 7a34fc589eabddbeaa13fc424552a1fbad23bae3 Mon Sep 17 00:00:00 2001 From: Lockywolf Date: Thu, 24 Jul 2025 21:07:10 +0800 Subject: [PATCH] ispell.el: Add 36 tests. * test/lisp/textmodes/ispell-tests/*: Add 36 tests for basic utils used in ispell.el. ispell-program-name ispell-with-safe-default-directory ispell-call-process ispell-create-debug-buffer ispell-valid-dictionary-list ispell-add-per-file-word-list ispell-buffer-local-words ispell-init-process ispell-buffer-local-dict * test/lisp/textmodes/ispell-resources/fake-aspell: Add a mock `aspell' for use in ispell.el test. --- .../ispell-resources/fake-aspell.bash | 2 + .../textmodes/ispell-tests/ispell-aspell.el | 70 ++ test/lisp/textmodes/ispell-tests/ispell.el | 600 ++++++++++++++++++ 3 files changed, 672 insertions(+) create mode 100755 test/lisp/textmodes/ispell-resources/fake-aspell.bash create mode 100644 test/lisp/textmodes/ispell-tests/ispell-aspell.el create mode 100644 test/lisp/textmodes/ispell-tests/ispell.el diff --git a/test/lisp/textmodes/ispell-resources/fake-aspell.bash b/test/lisp/textmodes/ispell-resources/fake-aspell.bash new file mode 100755 index 00000000000..4406a18a22e --- /dev/null +++ b/test/lisp/textmodes/ispell-resources/fake-aspell.bash @@ -0,0 +1,2 @@ +#!/bin/bash +printf '%s\n' "@(#) International Ispell Version 3.1.20 (but really Aspell 0.59.800)" diff --git a/test/lisp/textmodes/ispell-tests/ispell-aspell.el b/test/lisp/textmodes/ispell-tests/ispell-aspell.el new file mode 100644 index 00000000000..c0886ff53b8 --- /dev/null +++ b/test/lisp/textmodes/ispell-tests/ispell-aspell.el @@ -0,0 +1,70 @@ +;;; tests-ispell-aspell.el --- Test ispell.el aspell backend. -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Lockywolf + +;; Author: Lockywolf +;; Keywords: languages, text + +;; This program 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. + +;; This program 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 this program. If not, see . + +;;; Commentary: + +;; Tests for ispell.el's aspell integration. + +;;; Code: + +(require 'ispell) + +(defvar tests-ispell-data-directory + (expand-file-name "test/lisp/textmodes/ispell-resources/" source-directory)) + +(ert-deftest ispell-aspell-available-ispell-check-version () + "Test that aspell is correctly detected." + (skip-unless (and (executable-find "aspell") + (with-temp-buffer + (call-process "aspell" nil t nil "-vv") + (search-backward "but really Aspell")))) + (should (stringp + (let ((test-saved-ispell-program-name ispell-program-name)) + (unwind-protect + (let () + (setq ispell-last-program-name (time-to-seconds)) + (setf ispell-program-name "aspell") + ispell-really-aspell) + (setf ispell-program-name test-saved-ispell-program-name)))))) + +(ert-deftest ispell-aspell-available-ispell-check-version-error-low () + "Test that aspell is correctly detected." + (let ((fake-aspell-path (expand-file-name + "./fake-aspell.bash" + tests-ispell-data-directory))) + (chmod fake-aspell-path 504) + (call-process fake-aspell-path nil nil nil) + (let ((test-saved-ispell-program-name ispell-program-name) + (test-saved-ispell-last-program-name ispell-last-program-name)) + (unwind-protect + (progn + (setq ispell-last-program-name (time-to-seconds)) + (should-error + (progn + (setopt ispell-program-name fake-aspell-path) + (ispell-check-version t))) + ispell-really-aspell) + (set-variable 'ispell-program-name test-saved-ispell-program-name) + (set-variable 'ispell-last-program-name + test-saved-ispell-last-program-name))))) + + +(provide 'tests-ispell-aspell) +;;; tests-ispell-aspell.el ends here diff --git a/test/lisp/textmodes/ispell-tests/ispell.el b/test/lisp/textmodes/ispell-tests/ispell.el new file mode 100644 index 00000000000..6620e928333 --- /dev/null +++ b/test/lisp/textmodes/ispell-tests/ispell.el @@ -0,0 +1,600 @@ +;;; tests-ispell.el --- Test ispell.el. -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Lockywolf + +;; Author: Lockywolf +;; Keywords: languages, text + +;; This program 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. + +;; This program 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 this program. If not, see . + +;;; Commentary: + +;; Tests for ispell.el. + +;;; Code: + +(require 'ispell) + +(defun warnings-buffer-exists-p () + "Check if a buffer named \"*Warnings*\" exists." + (if (get-buffer "*Warnings*") + t + nil)) + +(ert-deftest ispell/ispell-program-name/nil () + "Sanity check. Setting a non-string should produce a warning. +Give ispell-program-name a wrong type." + (should (unwind-protect + (progn + (setq ispell-program-name "ispell") + (when (warnings-buffer-exists-p) + (kill-buffer "*Warnings*")) + (setopt ispell-program-name nil) + (if (warnings-buffer-exists-p) + t + nil)) + (when (warnings-buffer-exists-p) + (kill-buffer "*Warnings*"))))) + +(ert-deftest ispell/ispell-program-name/noncommand () + "Sanity check. Should error or at least warn. +Give ispell-program-name a meaningless string." + :expected-result :failed + (should-error + (setopt ispell-program-name "6c628ac4-63a0-11f0-b37c-e38fc166e3fc") ;; random nonexistent name + )) + +(ert-deftest ispell/ispell-program-name/noncommand/interactive () + "Sanity check. Should error or at least warn. +Give ispell-program-name a meaningless string." + (should-error + (progn + (setopt ispell-program-name "6c628ac4-63a0-11f0-b37c-e38fc166e3fc") ;; random nonexistent name + (ispell-check-version) + ))) + +(ert-deftest ispell/ispell-program-name/non-executable () + "Sanity check. Should error or at least warn. +Give ispell-program-name a path to a non-executable. +I personally think that this should always fail, but +at the moment only an interactive call fails." + :expected-result :failed + (should-error + (progn + (setopt ispell-program-name null-device)))) + +(ert-deftest ispell/ispell-program-name/non-executable/interactive () + "Sanity check. Should error or at least warn. +Give ispell-program-name a path to a non-executable." + (should-error + (progn + (setopt ispell-program-name null-device) + (ispell-check-version t)))) + +(ert-deftest ispell/ispell-program-name/non-spellchecker () + "Sanity check. Give ispell-program-name a path to a non-spellchecker. +Fails because for non-interactive runs, `ispell-check-version' does +not actually err." + :expected-result :failed + (skip-unless (executable-find "etags")) + (should-error (string-equal "etags" (setopt ispell-ispell-program "etags")))) + +(ert-deftest ispell/ispell-program-name/non-spellchecker/interactive () + "Sanity check. Give ispell-program-name a path to a non-spellchecker." + (skip-unless (executable-find "etags")) + (should-error + (progn (setopt ispell-ispell-program "etags") + (ispell-check-version t)) + )) + +(ert-deftest ispell/ispell-program-name/ispell () + "Sanity check. If at least some ispell is available, should pass. +Give ispell-program-name a real spellchecker" + (skip-unless (and (executable-find "ispell") + (with-temp-buffer + (call-process "ispell" nil t nil "-vv") + (search-backward "Ispell")))) + ;; should not throw + (should (string-equal "ispell" (setopt ispell-ispell-program "ispell")))) + +(ert-deftest ispell/ispell-with-safe-default-directory/bad () + "Try doing something with a bad default directory." + (should (with-temp-buffer + (let ((default-directory "c296752a-7d7b-4769-a2d4-4bfd96c7ca71")) + (ispell-with-safe-default-directory + (equal default-directory (expand-file-name "~/"))))))) + +(ert-deftest ispell/ispell-with-safe-default-directory/good () + "Try doing something with a bad default directory." + (should (with-temp-buffer + (let ((default-directory temporary-file-directory)) + (ispell-with-safe-default-directory + (equal default-directory temporary-file-directory)))))) + +(ert-deftest ispell/ispell-call-process/simple () + "Check that ispell-call-process works. +This test fails, because HOME is not defined. +This should not be the case, because ispell-call-process +whould be making sure that the directory for running +the backend's process exists." + :expected-result :failed + (should + (with-temp-buffer + (let ((default-directory "86e44985-cfba-43ba-98dc-73be46addbc2")) + (ispell-call-process "emacs" nil t nil '("--batch" "-q" "-nw" "--eval" "(progn (message default-directory) (kill-emacs))")) + (search-backward (expand-file-name "~")))))) + +(ert-deftest ispell/ispell-call-process/simple-writable () + "Check that ispell-call-process works." + (should + (with-temp-buffer + (let ((default-directory temporary-file-directory)) + (ispell-call-process "emacs" nil t nil "--batch" "-q" "-nw" "--eval" "(message default-directory)") + (message "lwf:%s" (buffer-string)) + (search-backward (directory-file-name temporary-file-directory)))))) + +(ert-deftest ispell/ispell-call-process-region/cat-empty () + "Check ispell-call-process-region works with unrelated process. +This test is expected to fail, because at the moment, there is +a construction (let ((default-directory default-directory))...) in +the `ispell-with-safe-default-directory' function, which effectively +makes it useless." + :expected-result :failed + (should + (with-temp-buffer + (let* ((string-to-send "") + (dir (concat temporary-file-directory + "86e44985-cfba-43ba-98dc-73be46addbc2"))) + (make-directory dir t) + (chmod dir 000) + (let ((default-directory dir)) + ;; (ispell-call-process-region string-to-send nil "cat" nil t nil) + (ispell-call-process-region "emacs" nil t nil "--batch" "-q" "-nw" "--eval" "(progn (setq this-read (ignore-errors (read-from-minibuffer \"\"))) (message \"%s\" this-read))") + ;; emacs --batch --eval '(progn (setq this-read (ignore-errors (read-from-minibuffer ""))) (message "%s" this-read))' + (equal (buffer-string) string-to-send)))))) + +(ert-deftest ispell/ispell-call-process-region/cat-random () + "Check ispell-call-process-region works with unrelad process. +This test is expected to fail, because at the moment, there is +a construction (let ((default-directory default-directory))...) in +the `ispell-with-safe-default-directory' function, which effectively +makes it useless." + :expected-result :failed + (should + (with-temp-buffer + (let ((string-to-send (format "%s" (random))) + (default-directory "86e44985-cfba-43ba-98dc-73be46addbc2")) + (ispell-call-process-region "emacs" nil t nil "--batch" "-q" "-nw" "--eval" "(progn (setq this-read (ignore-errors (read-from-minibuffer \"\"))) (message \"%s\" this-read))") + (equal (buffer-string) string-to-send))))) + +(ert-deftest ispell/ispell-create-debug-buffer () + "Make sure that debug buffer creation works." + (when (bufferp (get-buffer "*ispell-debug*")) + (with-current-buffer "*ispell-debug*" + (rename-buffer "*ispell-debug*-test"))) + (unwind-protect + (progn + (ispell-create-debug-buffer) + (should (bufferp (get-buffer "*ispell-debug*"))) + (kill-buffer "*ispell-debug*") ;; should not error + ) + (when (bufferp (get-buffer "*ispell-debug*-test")) + (with-current-buffer "*ispell-debug*-test" + (rename-buffer "*ispell-debug*")))) + ) + +;; FIXME: this test should probably go into a separate file, dedicated +;; to the hunspell backend, but so far there is not partition between +;; backends, so let us add it here. It is easy to move it. +(ert-deftest ispell/ispell-valid-dictionary-list/hunspell/no-library-directory () + "If hunspell, `ispell-valid-dictionary-list' returns default. +This function only works for aspell and ispell, for hunspell and +enchant-2 it always returns either default or everything. +I think this is an issue in itself, but this test is added to verify +that changes to third-party code do not break existing behaviour." + (skip-unless (executable-find "hunspell")) + (skip-unless (equal 0 (call-process "hunspell" nil nil nil))) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory)) + (unwind-protect + (progn + (setopt ispell-program-name "hunspell") + (setopt ispell-library-directory nil) + (ispell-check-version t) + (should + (equal + (sort (ispell-valid-dictionary-list) 'string<) + (sort (cl-substitute "default" nil (mapcar #'car ispell-dictionary-alist)) 'string<)))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell))) + ) + +;; FIXME: this test should probably go into a separate file, dedicated +;; to the hunspell backend, but so far there is not partition between +;; backends, so let us add it here. It is easy to move it. +(ert-deftest ispell/ispell-valid-dictionary-list/hunspell/library-directory () + "If hunspell, `ispell-valid-dictionary-list' returns default. +This function only works for aspell and ispell, for hunspell and +enchant-2 it always returns either default or everything. +I think this is an issue in itself, but this test is added to verify +that changes to third-party code do not break existing behaviour." + (skip-unless (executable-find "hunspell")) + (skip-unless (equal 0 (call-process "hunspell" nil nil nil))) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory)) + (unwind-protect + (progn + (setopt ispell-program-name "hunspell") + (ispell-check-version t) + (setopt ispell-library-directory "/tmp") + (should + (equal + (ispell-valid-dictionary-list) + '("default")))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell))) + ) + +;; FIXME: this test should probably go into a separate file, dedicated +;; to the enchant-2 backend, but so far there is not partition between +;; backends, so let us add it here. It is easy to move it. +(ert-deftest ispell/ispell-valid-dictionary-list/enchant-2/no-library-directory () + "If enchant-2, `ispell-valid-dictionary-list' returns default. +This function only works for aspell and ispell, for hunspell and +enchant-2 it always returns either default or everything. +I think this is an issue in itself, but this test is added to verify +that changes to third-party code do not break existing behaviour." + (skip-unless (executable-find "enchant-2")) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory)) + (unwind-protect + (progn + (setopt ispell-program-name "enchant-2") + (setopt ispell-library-directory nil) + (ispell-check-version t) + (should + (equal + (sort (ispell-valid-dictionary-list) 'string<) + (sort (cl-substitute "default" nil (mapcar #'car ispell-dictionary-alist)) 'string<)))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell))) + ) + +;; FIXME: this test should probably go into a separate file, dedicated +;; to the enchant-2 backend, but so far there is not partition between +;; backends, so let us add it here. It is easy to move it. +(ert-deftest ispell/ispell-valid-dictionary-list/enchant-2/library-directory () + "If enchant-2, `ispell-valid-dictionary-list' returns default. +This function only works for aspell and ispell, for hunspell and +enchant-2 it always returns either default or everything. +I think this is an issue in itself, but this test is added to verify +that changes to third-party code do not break existing behaviour." + (skip-unless (executable-find "enchant-2")) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory)) + (unwind-protect + (progn + (setopt ispell-program-name "enchant-2") + (setopt ispell-library-directory "/tmp") + (ispell-check-version t) + (should + (equal + (ispell-valid-dictionary-list) + '("default")))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell))) + ) + +(ert-deftest ispell/ispell-valid-dictionary-list/international-ispell () + "Check that ispell-valid-dictionary-list does something useful for ispell. +For ispell, `ispell-valid-dictionary-list' checks that a corresponding +file is present in `ispell-library-directory'." + (skip-unless (executable-find "ispell")) + (skip-unless (let ((libdir (with-temp-buffer + (call-process "ispell" nil t nil "-vv") + (goto-char (point-min)) + (when (re-search-forward + "LIBDIR *= *\"\\([^\"]+\\)\"" nil t) + (match-string 1))))) + (file-readable-p (expand-file-name "english.hash" libdir)))) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory) + (old-ispell-local-dictionary-alist ispell-local-dictionary-alist)) + (unwind-protect + (progn + (setopt ispell-program-name "ispell") ;; this should set ispell-library-directory + (ispell-check-version t) ;; sets ispell-library-directory + (should (not (null ispell-library-directory))) + ;; english is always shipped with international ispell, + ;; other languages not necessarily + (setopt ispell-local-dictionary-alist + '(("english" "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") nil utf-8))) + (should (equal '("english" "default") + (ispell-valid-dictionary-list)))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell) + (setopt ispell-local-dictionary-alist old-ispell-local-dictionary-alist)))) + +(ert-deftest ispell/ispell-valid-dictionary-list/aspell () + "Check that ispell-valid-dictionary-list does something useful for aspell. +For aspell, `ispell-valid-dictionary-list' computes an intersection of +`ispell-dictionary-alist' and `ispell--aspell-found-dictionaries'." + (skip-unless (executable-find "aspell")) + (skip-unless (with-temp-buffer + (call-process "aspell" nil t nil "dicts") + (> (length (buffer-string)) 2))) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory) + (old-ispell-local-dictionary-alist ispell-local-dictionary-alist) + (old-ispell-dictionary-alist ispell-dictionary-alist)) + (unwind-protect + (progn + (setopt ispell-program-name "aspell") ;; this should set ispell-library-directory + (ispell-check-version t) ;; sets ispell-library-directory + ;; english is always shipped with international ispell, + ;; other languages not necessarily + (setopt ispell-local-dictionary-alist + '(("english" "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") nil utf-8))) + (should + (> (length (ispell-valid-dictionary-list)) + (length ispell--aspell-found-dictionaries)))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-dictionary-alist old-ispell-dictionary-alist) + (setopt ispell-program-name old-ispell) + (setopt ispell-local-dictionary-alist old-ispell-local-dictionary-alist)))) + +;; Adding file-local words into the file. (They are _not_ sent to the +;; backend in this function.) + +(ert-deftest ispell/ispell-add-per-file-word-list/simple () + "Adding a per-file word to an empty buffer. No comment +syntax expected." + (with-temp-buffer + (let ((testword (format "%s" (random)))) + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) +(concat " +" ispell-words-keyword " " testword " +")))))) + +(ert-deftest ispell/ispell-add-per-file-word-list/comments () + "Adding a per-file word to an empty buffer. Uses default +emacs-lisp comment syntax." + (with-temp-buffer + (let ((testword (format "%s" (random)))) + (emacs-lisp-mode) + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) +(concat " +; " ispell-words-keyword " " testword " +")))))) + +(ert-deftest ispell/ispell-add-per-file-word-list/nxml () + "Adding a per-file word to an empty buffer. Uses default +xml comment syntax, which has an opening and a closing +marker." + (with-temp-buffer + (let ((testword (format "%s" (random)))) + (nxml-mode) + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) +(concat " + +")))))) + +(ert-deftest ispell/ispell-add-per-file-word-list/keyword-there-space () + "Adding a per-file word to buffer with keyword. Uses default +xml comment syntax, which has an opening and a closing +marker. " + (with-temp-buffer + (let ((testword (format "%s" (random)))) + (nxml-mode) + (insert " + +") + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) +(concat " + +")))))) + +(let* ((backend-binaries (list "ispell" "aspell" "hunspell" "enchant-2")) + (filter-binaries (lambda () + (seq-filter + #'executable-find + backend-binaries)))) + + (defun ispell-tests--some-backend-available-p () + (not + (null (funcall filter-binaries)))) + + (defun ispell-tests--some-backend () + (car (funcall filter-binaries)))) + +(cl-defmacro letopt (bindings &body body) + (declare (indent 1)) + (let* ((binding-var (lambda (binding) (car binding))) + (binding-val (lambda (binding) (cadr binding))) + (make-setopt (lambda (a b) + (list 'setopt a b))) + (vars (seq-map binding-var bindings)) + (values (seq-map binding-val bindings)) + (temp-vars (seq-map #'gensym vars)) + (savebindings (seq-mapn #'list temp-vars vars)) + (tempbindings (seq-mapn make-setopt vars values)) + (restorebindings (seq-mapn make-setopt vars temp-vars))) + `(let ,savebindings + (unwind-protect (progn ,@tempbindings + ,@body) + ,@(reverse restorebindings))))) + +(ert-deftest ispell/ispell-add-per-file-word-list/longline () + "Adding a per-file word to buffer with keyword. Uses default +xml comment syntax, which has an opening and a closing +marker. +This test fails, because ispell.el does not work well with +nXML comments." + :expected-result :failed + (letopt ((ispell-program-name "ispell")) + (with-temp-buffer + (let* ((testword (format "%s" (random))) + (fill-column 50)) + (nxml-mode) + (insert " + +") + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) +(concat " + +" +" + +"))))))) + +;; Adding file-local words from the file's cellar into the backend +;; (@-prefixed, see *man ispell*). (They _are_ sent to the backend in +;; this function.) + +(ert-deftest ispell/ispell-buffer-local-words/ispell-words-keyword () + "Send some words prefixed by @ from the file's cellar to backend. +Should pass regardless of the backend and the dictionary, because +presumably nobody will have `hellooooooo' in their dictionary." + (skip-unless (ispell-tests--some-backend-available-p)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (nxml-mode) + (ignore-errors (ispell-kill-ispell)) + (with-environment-variables (("HOME" temporary-file-directory)) + (ispell-init-process) + (let ((test-output (ispell--run-on-word "hellooooooo"))) + (should (listp test-output)) + (should-not (equal t test-output))) + (ispell-add-per-file-word-list "hellooooooo") + (ispell-buffer-local-words) + (should (equal t (ispell--run-on-word "hellooooooo"))))))) + + +(ert-deftest + ispell/ispell-buffer-local-words/ispell-buffer-session-localwords () + "Send some words prefixed by @ from the file's cellar to backend. +Should pass regardless of the backend and the dictionary, because +presumably nobody will have `hellooooooo' in their dictionary." + (skip-unless (ispell-tests--some-backend-available-p)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (cd temporary-file-directory) + (with-temp-buffer + (nxml-mode) + (ignore-errors (ispell-kill-ispell)) + (with-environment-variables (("HOME" temporary-file-directory)) + (ispell-init-process) + (let ((test-output (ispell--run-on-word "hellooooooo"))) + (should (listp test-output)) + (should-not (equal t test-output))) + (let ((ispell-buffer-session-localwords (list "hellooooooo"))) + (ispell-buffer-local-words) + (should (equal t (ispell--run-on-word "hellooooooo")))))))) + +(ert-deftest ispell/ispell-init-process/works-nohome () + "Simple test to check that ispell-init-process works." + :expected-result :failed + (skip-unless (ispell-tests--some-backend-available-p)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (ispell-init-process)))) + +(ert-deftest ispell/ispell-init-process/works-withhome () + "Simple test to check that ispell-init-process works." + (skip-unless (ispell-tests--some-backend-available-p)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (with-environment-variables (("HOME" temporary-file-directory)) + (ispell-init-process))))) + +;; Some more tests for buffer-local stuff. +;; `ispell-buffer-local-dict' + +(ert-deftest ispell/ispell-buffer-local-dict/no-reload+no-overriden () + "ispell.el can recognise keyword-defined dictionary and keyword-defined +personal-dictionary. With no-reload it needs no backend at all." + (with-temp-buffer + (nxml-mode) + (let ((test-dict "nonexistent") + (test-pdict "/tmp/lnonexistent.txt")) + (insert + "hello\n\n\n" + "" + "") + (ispell-buffer-local-dict t) + (should (equal ispell-local-dictionary test-dict)) + (should (equal ispell-local-pdict test-pdict))))) + +(ert-deftest ispell/ispell-buffer-local-dict/reload+no-overriden () + "ispell.el can recognise keyword-defined dictionary and keyword-defined +personal-dictionary. With no-reload it needs no backend at all." + (with-temp-buffer + (nxml-mode) + (let ((test-dict "nonexistent") + (test-pdict "/tmp/lnonexistent.txt")) + (insert + "hello\n\n\n" + "" + "") + (letopt ((ispell-current-dictionary "nonexistent2")) + (ispell-buffer-local-dict) + (should (equal ispell-current-dictionary test-dict)) + (should (equal ispell-current-personal-dictionary test-pdict)))))) + +(ert-deftest ispell/ispell-buffer-local-dict/no-reload+overriden () + "ispell.el can recognise keyword-defined dictionary and keyword-defined +personal-dictionary. With no-reload it needs no backend at all." + (with-temp-buffer + (nxml-mode) + (let ((test-dict "nonexistent") + (test-pdict "/tmp/lnonexistent.txt")) + (insert + "hello\n\n\n" + "" + "") + (letopt ((ispell-current-dictionary "nonexistent2")) + (let ((ispell-local-dictionary-overridden t)) + (ispell-buffer-local-dict t)) + (should-not (equal ispell-local-dictionary test-dict)) + (should (equal ispell-local-pdict test-pdict)))))) + +(ert-deftest ispell/ispell-buffer-local-dict/reload+overriden () + "ispell.el can recognise keyword-defined dictionary and keyword-defined +personal-dictionary. With no-reload it needs no backend at all." + (with-temp-buffer + (nxml-mode) + (let ((test-dict "nonexistent") + (test-pdict "/tmp/lnonexistent.txt")) + (insert + "hello\n\n\n" + "" + "") + (letopt ((ispell-current-dictionary "nonexistent2")) + (let ((ispell-local-dictionary-overridden t)) + (ispell-buffer-local-dict t)) + (should-not (equal ispell-current-dictionary test-dict)) + (should (equal ispell-current-personal-dictionary test-pdict)))))) + +(provide 'tests-ispell) +;;; tests-ispell.el ends here -- 2.46.4 --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Thu Aug 07 03:02:18 2025 Received: (at 79177) by debbugs.gnu.org; 7 Aug 2025 07:02:18 +0000 Received: from localhost ([127.0.0.1]:33298 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ujudk-0002tt-Qo for submit@debbugs.gnu.org; Thu, 07 Aug 2025 03:02:18 -0400 Received: from coconut.lockywolf.net ([2a04:c5c0:0:d7:f816:3eff:fe6b:287f]:34276) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1ujudh-0002tg-LI for 79177@debbugs.gnu.org; Thu, 07 Aug 2025 03:02:15 -0400 Received: from laptop.lockywolf.net (unknown [IPv6:2001:470:24:315::102]) by coconut.lockywolf.net (Postfix) with ESMTPSA id 85D28386AD; Thu, 7 Aug 2025 15:02:06 +0800 (CST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=lockywolf.net; s=2024-10-06; t=1754550130; bh=A+kT4StT4KjLnR4+aBz/ZHq9HJIu7fSiT1wa/SBFHec=; h=From:To:Cc:Subject:In-Reply-To:References:Date; b=kzlI6Wb/rD/fMVqAesTrfRj7hOqBcieQTivnUdbhkR+WNvFhdxfgNc+mr14U/Sbbg Fqx3rgLCyQtcTST3tQltDgma1Ha1RNwU4RRl7aHawoQHn0KOoPjP0FBRnXzV3VXwCQ g7s1ksqer8wLT86jv3O3SZ6KDmblGhjDKO94rVR0s3eLYN+ELd6SnUbRiS0GnRg5BS oqCrYuBwr0p2tVFG6tqG+ybuzW674276PeLDsFwxP3qedYvLUc9soiRTzMGu/0EhNd M61ltmYFuTBeSUrJXXUn0MoDaDq356YmQQyWNUmUWYlBFYFv8KPkr6f8n1Z7ZN2QQ2 frUCQdE0hRZVg== From: Lockywolf To: 79177@debbugs.gnu.org Subject: Re: bug#79177: Acknowledgement (31.0.50; [PATCH] [v4] Add tests to ispell.el) In-Reply-To: References: <87cy9aj5oi.fsf@laptop.lockywolf.net> User-Agent: mu4e 1.12.9; emacs 31.0.50 Date: Thu, 07 Aug 2025 15:01:59 +0800 Message-ID: <87wm7fvcm0.fsf@laptop.lockywolf.net> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Spam-Score: -0.0 (/) X-Debbugs-Envelope-To: 79177 Cc: Lockywolf X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: multipart/signed; boundary="==-=-="; micalg=pgp-sha256; protocol="application/pgp-signature" --==-=-= Content-Type: text/plain Updated version of the patch attached: --==-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iHUEARYIAB0WIQSWBebxCAoqaJP8N/j71sGyD+xtqgUCaJRPaQAKCRD71sGyD+xt qtLZAP4+v+dFAZMjgIqHaZBCBWG60Qk2Bb+5aP8Wp15w991jrgD+I+StG86feTXi u5uzIPocs1p9zZOgEd1sgo2VucEF4AA= =uW16 -----END PGP SIGNATURE----- --==-=-=-- --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-ispell.el-Add-39-tests.patch Content-Description: attachment >From a5a489689f5361c4a86749ac3c9b33e83b23e528 Mon Sep 17 00:00:00 2001 From: Lockywolf Date: Thu, 24 Jul 2025 21:07:10 +0800 Subject: [PATCH] ispell.el: Add 39 tests. * test/lisp/textmodes/ispell-tests/*: Add 36 tests for basic utils used in ispell.el. ispell-program-name ispell-with-safe-default-directory ispell-call-process ispell-create-debug-buffer ispell-valid-dictionary-list ispell-add-per-file-word-list ispell-buffer-local-words ispell-init-process ispell-buffer-local-dict * test/lisp/textmodes/ispell-resources/fake-aspell: Add a mock `aspell' for use in ispell.el test. --- .../ispell-resources/fake-aspell.bash | 2 + .../textmodes/ispell-tests/ispell-aspell.el | 70 ++ test/lisp/textmodes/ispell-tests/ispell.el | 770 ++++++++++++++++++ 3 files changed, 842 insertions(+) create mode 100755 test/lisp/textmodes/ispell-resources/fake-aspell.bash create mode 100644 test/lisp/textmodes/ispell-tests/ispell-aspell.el create mode 100644 test/lisp/textmodes/ispell-tests/ispell.el diff --git a/test/lisp/textmodes/ispell-resources/fake-aspell.bash b/test/lisp/textmodes/ispell-resources/fake-aspell.bash new file mode 100755 index 00000000000..4406a18a22e --- /dev/null +++ b/test/lisp/textmodes/ispell-resources/fake-aspell.bash @@ -0,0 +1,2 @@ +#!/bin/bash +printf '%s\n' "@(#) International Ispell Version 3.1.20 (but really Aspell 0.59.800)" diff --git a/test/lisp/textmodes/ispell-tests/ispell-aspell.el b/test/lisp/textmodes/ispell-tests/ispell-aspell.el new file mode 100644 index 00000000000..c0886ff53b8 --- /dev/null +++ b/test/lisp/textmodes/ispell-tests/ispell-aspell.el @@ -0,0 +1,70 @@ +;;; tests-ispell-aspell.el --- Test ispell.el aspell backend. -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Lockywolf + +;; Author: Lockywolf +;; Keywords: languages, text + +;; This program 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. + +;; This program 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 this program. If not, see . + +;;; Commentary: + +;; Tests for ispell.el's aspell integration. + +;;; Code: + +(require 'ispell) + +(defvar tests-ispell-data-directory + (expand-file-name "test/lisp/textmodes/ispell-resources/" source-directory)) + +(ert-deftest ispell-aspell-available-ispell-check-version () + "Test that aspell is correctly detected." + (skip-unless (and (executable-find "aspell") + (with-temp-buffer + (call-process "aspell" nil t nil "-vv") + (search-backward "but really Aspell")))) + (should (stringp + (let ((test-saved-ispell-program-name ispell-program-name)) + (unwind-protect + (let () + (setq ispell-last-program-name (time-to-seconds)) + (setf ispell-program-name "aspell") + ispell-really-aspell) + (setf ispell-program-name test-saved-ispell-program-name)))))) + +(ert-deftest ispell-aspell-available-ispell-check-version-error-low () + "Test that aspell is correctly detected." + (let ((fake-aspell-path (expand-file-name + "./fake-aspell.bash" + tests-ispell-data-directory))) + (chmod fake-aspell-path 504) + (call-process fake-aspell-path nil nil nil) + (let ((test-saved-ispell-program-name ispell-program-name) + (test-saved-ispell-last-program-name ispell-last-program-name)) + (unwind-protect + (progn + (setq ispell-last-program-name (time-to-seconds)) + (should-error + (progn + (setopt ispell-program-name fake-aspell-path) + (ispell-check-version t))) + ispell-really-aspell) + (set-variable 'ispell-program-name test-saved-ispell-program-name) + (set-variable 'ispell-last-program-name + test-saved-ispell-last-program-name))))) + + +(provide 'tests-ispell-aspell) +;;; tests-ispell-aspell.el ends here diff --git a/test/lisp/textmodes/ispell-tests/ispell.el b/test/lisp/textmodes/ispell-tests/ispell.el new file mode 100644 index 00000000000..00c0f29456a --- /dev/null +++ b/test/lisp/textmodes/ispell-tests/ispell.el @@ -0,0 +1,770 @@ +;;; tests-ispell.el --- Test ispell.el. -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Lockywolf + +;; Author: Lockywolf +;; Keywords: languages, text + +;; This program 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. + +;; This program 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 this program. If not, see . + +;;; Commentary: + +;; Tests for ispell.el. + +;;; Code: + +(require 'ispell) + +(defun warnings-buffer-exists-p () + "Check if a buffer named \"*Warnings*\" exists." + (if (get-buffer "*Warnings*") + t + nil)) + +(ert-deftest ispell/ispell-program-name/nil () + "Sanity check. Setting a non-string should produce a warning. +Give ispell-program-name a wrong type." + (should (unwind-protect + (progn + (setq ispell-program-name "ispell") + (when (warnings-buffer-exists-p) + (kill-buffer "*Warnings*")) + (setopt ispell-program-name nil) + (if (warnings-buffer-exists-p) + t + nil)) + (when (warnings-buffer-exists-p) + (kill-buffer "*Warnings*"))))) + +(ert-deftest ispell/ispell-program-name/noncommand () + "Sanity check. Should error or at least warn. +Give ispell-program-name a meaningless string." + :expected-result :failed + (should-error + (setopt ispell-program-name "6c628ac4-63a0-11f0-b37c-e38fc166e3fc") ;; random nonexistent name + )) + +(ert-deftest ispell/ispell-program-name/noncommand/interactive () + "Sanity check. Should error or at least warn. +Give ispell-program-name a meaningless string." + (should-error + (progn + (setopt ispell-program-name "6c628ac4-63a0-11f0-b37c-e38fc166e3fc") ;; random nonexistent name + (ispell-check-version) + ))) + +(ert-deftest ispell/ispell-program-name/non-executable () + "Sanity check. Should error or at least warn. +Give ispell-program-name a path to a non-executable. +I personally think that this should always fail, but +at the moment only an interactive call fails." + :expected-result :failed + (should-error + (progn + (setopt ispell-program-name null-device)))) + +(ert-deftest ispell/ispell-program-name/non-executable/interactive () + "Sanity check. Should error or at least warn. +Give ispell-program-name a path to a non-executable." + (should-error + (progn + (setopt ispell-program-name null-device) + (ispell-check-version t)))) + +(ert-deftest ispell/ispell-program-name/non-spellchecker () + "Sanity check. Give ispell-program-name a path to a non-spellchecker. +Fails because for non-interactive runs, `ispell-check-version' does +not actually err." + :expected-result :failed + (skip-unless (executable-find "etags")) + (should-error (string-equal "etags" (setopt ispell-ispell-program "etags")))) + +(ert-deftest ispell/ispell-program-name/non-spellchecker/interactive () + "Sanity check. Give ispell-program-name a path to a non-spellchecker." + (skip-unless (executable-find "etags")) + (should-error + (progn (setopt ispell-ispell-program "etags") + (ispell-check-version t)) + )) + +(ert-deftest ispell/ispell-program-name/ispell () + "Sanity check. If at least some ispell is available, should pass. +Give ispell-program-name a real spellchecker" + (skip-unless (and (executable-find "ispell") + (with-temp-buffer + (call-process "ispell" nil t nil "-vv") + (search-backward "Ispell")))) + ;; should not throw + (should (string-equal "ispell" (setopt ispell-ispell-program "ispell")))) + +(ert-deftest ispell/ispell-with-safe-default-directory/bad () + "Try doing something with a bad default directory." + (should (with-temp-buffer + (let ((default-directory "c296752a-7d7b-4769-a2d4-4bfd96c7ca71")) + (ispell-with-safe-default-directory + (equal default-directory (expand-file-name "~/"))))))) + +(ert-deftest ispell/ispell-with-safe-default-directory/good () + "Try doing something with a bad default directory." + (should (with-temp-buffer + (let ((default-directory temporary-file-directory)) + (ispell-with-safe-default-directory + (equal default-directory temporary-file-directory)))))) + +(ert-deftest ispell/ispell-call-process/simple () + "Check that ispell-call-process works. +This test fails, because HOME is not defined. +This should not be the case, because ispell-call-process +whould be making sure that the directory for running +the backend's process exists." + :expected-result :failed + (should + (with-temp-buffer + (let ((default-directory "86e44985-cfba-43ba-98dc-73be46addbc2")) + (ispell-call-process "emacs" nil t nil '("--batch" "-q" "-nw" "--eval" "(progn (message default-directory) (kill-emacs))")) + (search-backward (expand-file-name "~")))))) + +(ert-deftest ispell/ispell-call-process/simple-writable () + "Check that ispell-call-process works." + (should + (with-temp-buffer + (let ((default-directory temporary-file-directory)) + (ispell-call-process "emacs" nil t nil "--batch" "-q" "-nw" "--eval" "(message default-directory)") + (message "lwf:%s" (buffer-string)) + (search-backward (directory-file-name temporary-file-directory)))))) + +(ert-deftest ispell/ispell-call-process-region/cat-empty () + "Check ispell-call-process-region works with unrelated process. +This test is expected to fail, because at the moment, there is +a construction (let ((default-directory default-directory))...) in +the `ispell-with-safe-default-directory' function, which effectively +makes it useless." + :expected-result :failed + (should + (with-temp-buffer + (let* ((string-to-send "") + (dir (concat temporary-file-directory + "86e44985-cfba-43ba-98dc-73be46addbc2"))) + (make-directory dir t) + (chmod dir 000) + (let ((default-directory dir)) + ;; (ispell-call-process-region string-to-send nil "cat" nil t nil) + (ispell-call-process-region "emacs" nil t nil "--batch" "-q" "-nw" "--eval" "(progn (setq this-read (ignore-errors (read-from-minibuffer \"\"))) (message \"%s\" this-read))") + ;; emacs --batch --eval '(progn (setq this-read (ignore-errors (read-from-minibuffer ""))) (message "%s" this-read))' + (equal (buffer-string) string-to-send)))))) + +(ert-deftest ispell/ispell-call-process-region/cat-random () + "Check ispell-call-process-region works with unrelad process. +This test is expected to fail, because at the moment, there is +a construction (let ((default-directory default-directory))...) in +the `ispell-with-safe-default-directory' function, which effectively +makes it useless." + :expected-result :failed + (should + (with-temp-buffer + (let ((string-to-send (format "%s" (random))) + (default-directory "86e44985-cfba-43ba-98dc-73be46addbc2")) + (ispell-call-process-region "emacs" nil t nil "--batch" "-q" "-nw" "--eval" "(progn (setq this-read (ignore-errors (read-from-minibuffer \"\"))) (message \"%s\" this-read))") + (equal (buffer-string) string-to-send))))) + +(ert-deftest ispell/ispell-create-debug-buffer () + "Make sure that debug buffer creation works." + (when (bufferp (get-buffer "*ispell-debug*")) + (with-current-buffer "*ispell-debug*" + (rename-buffer "*ispell-debug*-test"))) + (unwind-protect + (progn + (ispell-create-debug-buffer) + (should (bufferp (get-buffer "*ispell-debug*"))) + (kill-buffer "*ispell-debug*") ;; should not error + ) + (when (bufferp (get-buffer "*ispell-debug*-test")) + (with-current-buffer "*ispell-debug*-test" + (rename-buffer "*ispell-debug*")))) + ) + +;; FIXME: this test should probably go into a separate file, dedicated +;; to the hunspell backend, but so far there is not partition between +;; backends, so let us add it here. It is easy to move it. +(ert-deftest ispell/ispell-valid-dictionary-list/hunspell/no-library-directory () + "If hunspell, `ispell-valid-dictionary-list' returns default. +This function only works for aspell and ispell, for hunspell and +enchant-2 it always returns either default or everything. +I think this is an issue in itself, but this test is added to verify +that changes to third-party code do not break existing behaviour." + (skip-unless (executable-find "hunspell")) + (skip-unless (equal 0 (call-process "hunspell" nil nil nil))) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory)) + (unwind-protect + (progn + (setopt ispell-program-name "hunspell") + (setopt ispell-library-directory nil) + (ispell-check-version t) + (should + (equal + (sort (ispell-valid-dictionary-list) 'string<) + (sort (cl-substitute "default" nil (mapcar #'car ispell-dictionary-alist)) 'string<)))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell))) + ) + +;; FIXME: this test should probably go into a separate file, dedicated +;; to the hunspell backend, but so far there is not partition between +;; backends, so let us add it here. It is easy to move it. +(ert-deftest ispell/ispell-valid-dictionary-list/hunspell/library-directory () + "If hunspell, `ispell-valid-dictionary-list' returns default. +This function only works for aspell and ispell, for hunspell and +enchant-2 it always returns either default or everything. +I think this is an issue in itself, but this test is added to verify +that changes to third-party code do not break existing behaviour." + (skip-unless (executable-find "hunspell")) + (skip-unless (equal 0 (call-process "hunspell" nil nil nil))) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory)) + (unwind-protect + (progn + (setopt ispell-program-name "hunspell") + (ispell-check-version t) + (setopt ispell-library-directory "/tmp") + (should + (equal + (ispell-valid-dictionary-list) + '("default")))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell))) + ) + +;; FIXME: this test should probably go into a separate file, dedicated +;; to the enchant-2 backend, but so far there is not partition between +;; backends, so let us add it here. It is easy to move it. +(ert-deftest ispell/ispell-valid-dictionary-list/enchant-2/no-library-directory () + "If enchant-2, `ispell-valid-dictionary-list' returns default. +This function only works for aspell and ispell, for hunspell and +enchant-2 it always returns either default or everything. +I think this is an issue in itself, but this test is added to verify +that changes to third-party code do not break existing behaviour." + (skip-unless (executable-find "enchant-2")) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory)) + (unwind-protect + (progn + (setopt ispell-program-name "enchant-2") + (setopt ispell-library-directory nil) + (ispell-check-version t) + (should + (equal + (sort (ispell-valid-dictionary-list) 'string<) + (sort (cl-substitute "default" nil (mapcar #'car ispell-dictionary-alist)) 'string<)))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell))) + ) + +;; FIXME: this test should probably go into a separate file, dedicated +;; to the enchant-2 backend, but so far there is not partition between +;; backends, so let us add it here. It is easy to move it. +(ert-deftest ispell/ispell-valid-dictionary-list/enchant-2/library-directory () + "If enchant-2, `ispell-valid-dictionary-list' returns default. +This function only works for aspell and ispell, for hunspell and +enchant-2 it always returns either default or everything. +I think this is an issue in itself, but this test is added to verify +that changes to third-party code do not break existing behaviour." + (skip-unless (executable-find "enchant-2")) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory)) + (unwind-protect + (progn + (setopt ispell-program-name "enchant-2") + (setopt ispell-library-directory "/tmp") + (ispell-check-version t) + (should + (equal + (ispell-valid-dictionary-list) + '("default")))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell))) + ) + +(ert-deftest ispell/ispell-valid-dictionary-list/international-ispell () + "Check that ispell-valid-dictionary-list does something useful for ispell. +For ispell, `ispell-valid-dictionary-list' checks that a corresponding +file is present in `ispell-library-directory'." + (skip-unless (executable-find "ispell")) + (skip-unless (let ((libdir (with-temp-buffer + (call-process "ispell" nil t nil "-vv") + (goto-char (point-min)) + (when (re-search-forward + "LIBDIR *= *\"\\([^\"]+\\)\"" nil t) + (match-string 1))))) + (file-readable-p (expand-file-name "english.hash" libdir)))) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory) + (old-ispell-local-dictionary-alist ispell-local-dictionary-alist)) + (unwind-protect + (progn + (setopt ispell-program-name "ispell") ;; this should set ispell-library-directory + (ispell-check-version t) ;; sets ispell-library-directory + (should (not (null ispell-library-directory))) + ;; english is always shipped with international ispell, + ;; other languages not necessarily + (setopt ispell-local-dictionary-alist + '(("english" "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") nil utf-8))) + (should (equal '("english" "default") + (ispell-valid-dictionary-list)))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell) + (setopt ispell-local-dictionary-alist old-ispell-local-dictionary-alist)))) + +(ert-deftest ispell/ispell-valid-dictionary-list/aspell () + "Check that ispell-valid-dictionary-list does something useful for aspell. +For aspell, `ispell-valid-dictionary-list' computes an intersection of +`ispell-dictionary-alist' and `ispell--aspell-found-dictionaries'." + (skip-unless (executable-find "aspell")) + (skip-unless (with-temp-buffer + (call-process "aspell" nil t nil "dicts") + (> (length (buffer-string)) 2))) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory) + (old-ispell-local-dictionary-alist ispell-local-dictionary-alist) + (old-ispell-dictionary-alist ispell-dictionary-alist)) + (unwind-protect + (progn + (setopt ispell-program-name "aspell") ;; this should set ispell-library-directory + (ispell-check-version t) ;; sets ispell-library-directory + ;; english is always shipped with international ispell, + ;; other languages not necessarily + (setopt ispell-local-dictionary-alist + '(("english" "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") nil utf-8))) + (should + (> (length (ispell-valid-dictionary-list)) + (length ispell--aspell-found-dictionaries)))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-dictionary-alist old-ispell-dictionary-alist) + (setopt ispell-program-name old-ispell) + (setopt ispell-local-dictionary-alist old-ispell-local-dictionary-alist)))) + +;; Adding file-local words into the file. (They are _not_ sent to the +;; backend in this function.) + +(ert-deftest ispell/ispell-add-per-file-word-list/simple () + "Adding a per-file word to an empty buffer. No comment +syntax expected." + (with-temp-buffer + (let ((testword (format "%s" (random)))) + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) +(concat " +" ispell-words-keyword " " testword " +")))))) + +(ert-deftest ispell/ispell-add-per-file-word-list/comments () + "Adding a per-file word to an empty buffer. Uses default +emacs-lisp comment syntax." + (with-temp-buffer + (let ((testword (format "%s" (random)))) + (emacs-lisp-mode) + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) +(concat " +; " ispell-words-keyword " " testword " +")))))) + +(ert-deftest ispell/ispell-add-per-file-word-list/nxml () + "Adding a per-file word to an empty buffer. Uses default +xml comment syntax, which has an opening and a closing +marker." + (with-temp-buffer + (let ((testword (format "%s" (random)))) + (nxml-mode) + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) +(concat " + +")))))) + +(ert-deftest ispell/ispell-add-per-file-word-list/keyword-there-space () + "Adding a per-file word to buffer with keyword. Uses default +xml comment syntax, which has an opening and a closing +marker. " + (with-temp-buffer + (let ((testword (format "%s" (random)))) + (nxml-mode) + (insert " + +") + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) +(concat " + +")))))) + +(let* ((backend-binaries (list "ispell" "aspell" "hunspell" "enchant-2")) + (filter-binaries (lambda () + (seq-filter + #'executable-find + backend-binaries)))) + + (defun ispell-tests--some-backend-available-p () + (not + (null (funcall filter-binaries)))) + + (defun ispell-tests--some-backend () + (car (funcall filter-binaries)))) + +(cl-defmacro letopt (bindings &body body) + (declare (indent 1)) + (let* ((binding-var (lambda (binding) (car binding))) + (binding-val (lambda (binding) (cadr binding))) + (make-setopt (lambda (a b) + (list 'setopt a b))) + (vars (seq-map binding-var bindings)) + (values (seq-map binding-val bindings)) + (temp-vars (seq-map #'gensym vars)) + (savebindings (seq-mapn #'list temp-vars vars)) + (tempbindings (seq-mapn make-setopt vars values)) + (restorebindings (seq-mapn make-setopt vars temp-vars))) + `(let ,savebindings + (unwind-protect (progn ,@tempbindings + ,@body) + ,@(reverse restorebindings))))) + +(ert-deftest ispell/ispell-add-per-file-word-list/longline () + "Adding a per-file word to buffer with keyword. Uses default +xml comment syntax, which has an opening and a closing +marker. +This test fails, because ispell.el does not work well with +nXML comments." + :expected-result :failed + (letopt ((ispell-program-name "ispell")) + (with-temp-buffer + (let* ((testword (format "%s" (random))) + (fill-column 50)) + (nxml-mode) + (insert " + +") + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) +(concat " + +" +" + +"))))))) + +;; Adding file-local words from the file's cellar into the backend +;; (@-prefixed, see *man ispell*). (They _are_ sent to the backend in +;; this function.) + +(ert-deftest ispell/ispell-buffer-local-words/ispell-words-keyword () + "Send some words prefixed by @ from the file's cellar to backend. +Should pass regardless of the backend and the dictionary, because +presumably nobody will have `hellooooooo' in their dictionary." + (skip-unless (ispell-tests--some-backend-available-p)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (nxml-mode) + (ignore-errors (ispell-kill-ispell)) + (with-environment-variables (("HOME" temporary-file-directory)) + (ispell-init-process) + (let ((test-output (ispell--run-on-word "hellooooooo"))) + (should (listp test-output)) + (should-not (equal t test-output))) + (ispell-add-per-file-word-list "hellooooooo") + (ispell-buffer-local-words) + (should (equal t (ispell--run-on-word "hellooooooo"))))))) + + +(ert-deftest + ispell/ispell-buffer-local-words/ispell-buffer-session-localwords () + "Send some words prefixed by @ from the file's cellar to backend. +Should pass regardless of the backend and the dictionary, because +presumably nobody will have `hellooooooo' in their dictionary." + (skip-unless (ispell-tests--some-backend-available-p)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (cd temporary-file-directory) + (with-temp-buffer + (nxml-mode) + (ignore-errors (ispell-kill-ispell)) + (with-environment-variables (("HOME" temporary-file-directory)) + (ispell-init-process) + (let ((test-output (ispell--run-on-word "hellooooooo"))) + (should (listp test-output)) + (should-not (equal t test-output))) + (let ((ispell-buffer-session-localwords (list "hellooooooo"))) + (ispell-buffer-local-words) + (should (equal t (ispell--run-on-word "hellooooooo")))))))) + +(ert-deftest ispell/ispell-init-process/works-nohome () + "Simple test to check that ispell-init-process works." + :expected-result :failed + (skip-unless (ispell-tests--some-backend-available-p)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (ispell-init-process)))) + +(ert-deftest ispell/ispell-init-process/works-withhome () + "Simple test to check that ispell-init-process works." + (skip-unless (ispell-tests--some-backend-available-p)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (with-environment-variables (("HOME" temporary-file-directory)) + (ispell-init-process))))) + +;; Some more tests for buffer-local stuff. +;; `ispell-buffer-local-dict' + +(ert-deftest ispell/ispell-buffer-local-dict/no-reload+no-overriden () + "ispell.el can recognise keyword-defined dictionary and keyword-defined +personal-dictionary. With no-reload it needs no backend at all." + (with-temp-buffer + (nxml-mode) + (let ((test-dict "nonexistent") + (test-pdict "/tmp/lnonexistent.txt")) + (insert + "hello\n\n\n" + "" + "") + (ispell-buffer-local-dict t) + (should (equal ispell-local-dictionary test-dict)) + (should (equal ispell-local-pdict test-pdict))))) + +(ert-deftest ispell/ispell-buffer-local-dict/reload+no-overriden () + "ispell.el can recognise keyword-defined dictionary and keyword-defined +personal-dictionary. With no-reload it needs no backend at all." + (with-temp-buffer + (nxml-mode) + (let ((test-dict "nonexistent") + (test-pdict "/tmp/lnonexistent.txt")) + (insert + "hello\n\n\n" + "" + "") + (letopt ((ispell-current-dictionary "nonexistent2")) + (ispell-buffer-local-dict) + (should (equal ispell-current-dictionary test-dict)) + (should (equal ispell-current-personal-dictionary test-pdict)))))) + +(ert-deftest ispell/ispell-buffer-local-dict/no-reload+overriden () + "ispell.el can recognise keyword-defined dictionary and keyword-defined +personal-dictionary. With no-reload it needs no backend at all." + (with-temp-buffer + (nxml-mode) + (let ((test-dict "nonexistent") + (test-pdict "/tmp/lnonexistent.txt")) + (insert + "hello\n\n\n" + "" + "") + (letopt ((ispell-current-dictionary "nonexistent2")) + (let ((ispell-local-dictionary-overridden t)) + (ispell-buffer-local-dict t)) + (should-not (equal ispell-local-dictionary test-dict)) + (should (equal ispell-local-pdict test-pdict)))))) + +(ert-deftest ispell/ispell-buffer-local-dict/reload+overriden () + "ispell.el can recognise keyword-defined dictionary and keyword-defined +personal-dictionary. With no-reload it needs no backend at all." + (with-temp-buffer + (nxml-mode) + (let ((test-dict "nonexistent") + (test-pdict "/tmp/lnonexistent.txt")) + (insert + "hello\n\n\n" + "" + "") + (letopt ((ispell-current-dictionary "nonexistent2")) + (let ((ispell-local-dictionary-overridden t)) + (ispell-buffer-local-dict t)) + (should-not (equal ispell-current-dictionary test-dict)) + (should (equal ispell-current-personal-dictionary test-pdict)))))) + +;; parsing + +(ert-deftest ispell/ispell-buffer-local-parsing/local-keyword () + "Check that ispell.el can suscessfully pick up a tex parser +from a buffer-local keyword." + ;; FIXME: what if default dictionary sets + ;; (ispell-get-extended-character-mode)? + (with-temp-buffer + (let ((test-parser "~tex") + (test-dictname "testdict") + (test-extcharmode "~latin3")) + (letopt ((ispell-parser 'nonexistent) + (ispell-local-dictionary-alist + `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8))) + (ispell-current-dictionary test-dictname)) + + (insert + "hello\n\n\n" ispell-parsing-keyword test-parser) + (let* ((counter 0)) + (cl-labels ((checker (s) + (setq counter (+ 1 counter)) + (when (equal counter 1) + (should (string-equal s "!\n"))) + (when (equal counter 2) + (should (string-equal s "-\n"))) + (when (equal counter 3) + (should (string-equal s (concat test-extcharmode "\n")))) + (when (equal counter 4) + (should (string-equal s (concat test-parser "\n")))) + t)) + (unwind-protect (progn + (advice-add 'ispell-send-string :override + #'checker) + (ispell-buffer-local-parsing)) + (advice-remove 'ispell-send-string #'checker))))))) + ) + +(ert-deftest ispell/ispell-buffer-local-parsing/mode-tex () + "Check that ispell.el can suscessfully pick up a tex parser +from tex-based mode-name. +There is another implicit check here: explicit-character-mode +(argument 7 from the ispell.el dictionary structure) is nil." + (with-temp-buffer + (let ((test-parser "~tex") + (test-dictname "testdict") + (test-extcharmode nil)) + (letopt ((ispell-check-comments t) + (ispell-parser 'use-mode-name) + (ispell-local-dictionary-alist + `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8))) + (ispell-current-dictionary test-dictname)) + (insert + "hello\n\n\n") + (tex-mode) + (let* ((counter 0)) + (cl-labels ((checker (s) + (setq counter (+ 1 counter)) + (when (equal counter 1) + (should (string-equal s "!\n"))) + (when (equal counter 2) + (should (string-equal s "+\n"))) + (when (equal counter 3) + (error "Should not have a third call to `ispell-send-string'")) + t)) + (unwind-protect (progn + (advice-add 'ispell-send-string :override + #'checker) + (ispell-buffer-local-parsing)) + (advice-remove 'ispell-send-string #'checker))))))) + ) + +(ert-deftest ispell/ispell-buffer-local-parsing/extended-character-mode () + "Check that ispell.el can suscessfully pick up an extended character +mode from the dictionary." + (with-temp-buffer + (insert + "hello\n\n\n") + (let ((test-extcharmode "~latin3")) + (letopt ((ispell-check-comments t) + (ispell-parser 'use-mode-name) + ;; FIXME: what if default dictionary sets + ;; (ispell-get-extended-character-mode)? + (ispell-local-dictionary-alist + `(("english" "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8))) + ) + (tex-mode) + (let* ((counter 0)) + (cl-labels ((checker (s) + (setq counter (+ 1 counter)) + (when (equal counter 1) + (should (string-equal s "!\n"))) + (when (equal counter 2) + (should (string-equal s "+\n"))) + (when (equal counter 3) + (should (string-equal s (concat test-extcharmode "\n")))) + (when (equal counter 4) + (error "Should not have a third call to `ispell-send-string'")) + t)) + (unwind-protect (progn + (advice-add 'ispell-send-string :override + #'checker) + (ispell-buffer-local-parsing)) + (advice-remove 'ispell-send-string #'checker))))))) + ) + +;; Let us now test the most important state-related function: +;; `ispell-accept-buffer-local-defs'. +;; Why is it important? +;; Because it is used in emacs' own CI for testing documentation +;; in checkdoc. +;; Indeed, when we are running the checker in batch mode, +;; we do not want to have any global state. + + +(ert-deftest ispell/ispell-accept-buffer-local-defs/simple () + "Check that `ispell-accept-buffer-local-defs' works for a +batch mode. +1. local words +2. dictionary and pdict +3. parser and extcharmode" + (with-temp-buffer + (let ((test-dictname "english") + (test-extcharmode "~latin3") + (test-parser "~testparser") + (test-localword1 "aaaaaaaaaaaaa") + (test-localword2 "bbbbbbbbbbb") + (test-pdict "test-pdict.pdict")) + (insert + "hello\n\n\n" + ispell-dictionary-keyword test-dictname "\n" + ispell-pdict-keyword (expand-file-name test-pdict temporary-file-directory) "\n" + ispell-parsing-keyword test-parser "\n" + ispell-words-keyword test-localword1 " " test-localword2 "\n") + (letopt ((ispell-check-comments t) + (ispell-parser 'tex) + ;; FIXME: what if default dictionary sets + ;; (ispell-get-extended-character-mode)? + (ispell-local-dictionary-alist + `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8)))) + (tex-mode) + (let* ((counter 0)) + (cl-labels ((checker-ispell-send-string (s) + (let ((references + (list nil + (concat "@" test-localword1 "\n") + (concat "@" test-localword2 "\n") + "!\n" + "+\n" + (concat test-extcharmode "\n") + (concat test-parser "\n")))) + (setq counter (+ 1 counter)) + (should (<= counter (length references))) + (should (string-equal + s + (nth counter references))) + t))) + (unwind-protect (progn + (advice-add 'ispell-send-string :before + #'checker-ispell-send-string) + (ispell-accept-buffer-local-defs) + (should (equal ispell-local-dictionary test-dictname)) + (should (equal ispell-local-pdict (expand-file-name test-pdict temporary-file-directory))) + ) + (advice-remove 'ispell-send-string #'checker-ispell-send-string))))))) + ) + +(provide 'tests-ispell) +;;; tests-ispell.el ends here -- 2.46.4 --=-=-= Content-Type: text/plain -- Your sincerely, Vladimir Nikishkin (MiEr, lockywolf) (Laptop) --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Thu Aug 07 03:36:15 2025 Received: (at 79177) by debbugs.gnu.org; 7 Aug 2025 07:36:15 +0000 Received: from localhost ([127.0.0.1]:33413 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ujvAd-0004ix-AL for submit@debbugs.gnu.org; Thu, 07 Aug 2025 03:36:15 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:55042) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1ujvAb-0004ih-D0 for 79177@debbugs.gnu.org; Thu, 07 Aug 2025 03:36:14 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ujvAV-0005Kp-Mp; Thu, 07 Aug 2025 03:36:07 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date: mime-version; bh=FiCH2W6AAWIw1tlFmlAw6mBt/07gX92AJjRFKyx/Wa4=; b=nvQMNPOXnVJt HbzdiVKHzAtsBQfE1odTJEqJzeAnAduDbWXV+4FR3vauz03Rcbe86DGqXWcJ3NTIDZByS1lo4XecC V7xEPVPYjcWvxikGoOe06MsOWEu0+Zt2NeXzncnBVLlNez9MjKPLxGWzjHWgpg6jBZm3WMoWNRE0o 8zlSfFT2QO1EkFMwmQAx64ZEKrK8Xgbms64V6oeustZFsgOXJcCsc7qVH/F/y+cbD8q7vv9G+Owl+ wxw9DWsPx2GYSf22s8eglv9YDmG6wbG9RTJbo4C20Q/NxuOdpTCu7HSpYbAmH8ObuIl/ApU2ipIYk /ZAul59SPJEZJFe8/sgYrw==; Date: Thu, 07 Aug 2025 10:36:04 +0300 Message-Id: <86v7mzr3bv.fsf@gnu.org> From: Eli Zaretskii To: Lockywolf In-Reply-To: <87wm7fvcm0.fsf@laptop.lockywolf.net> (message from Lockywolf on Thu, 07 Aug 2025 15:01:59 +0800) Subject: Re: bug#79177: 31.0.50; [PATCH] [v4] Add tests to ispell.el References: <87cy9aj5oi.fsf@laptop.lockywolf.net> <87wm7fvcm0.fsf@laptop.lockywolf.net> X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 79177 Cc: 79177@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > Cc: Lockywolf > From: Lockywolf > Date: Thu, 07 Aug 2025 15:01:59 +0800 > > Updated version of the patch attached: Thanks, comments below. > >From a5a489689f5361c4a86749ac3c9b33e83b23e528 Mon Sep 17 00:00:00 2001 > From: Lockywolf > Date: Thu, 24 Jul 2025 21:07:10 +0800 > Subject: [PATCH] ispell.el: Add 39 tests. > > * test/lisp/textmodes/ispell-tests/*: Add 36 tests for basic utils > used in ispell.el. > ispell-program-name > ispell-with-safe-default-directory > ispell-call-process > ispell-create-debug-buffer > ispell-valid-dictionary-list > ispell-add-per-file-word-list > ispell-buffer-local-words > ispell-init-process > ispell-buffer-local-dict > * test/lisp/textmodes/ispell-resources/fake-aspell: Add a mock > `aspell' for use in ispell.el test. Please mention the bug number in the commit log message. > diff --git a/test/lisp/textmodes/ispell-resources/fake-aspell.bash b/test/lisp/textmodes/ispell-resources/fake-aspell.bash > new file mode 100755 > index 00000000000..4406a18a22e > --- /dev/null > +++ b/test/lisp/textmodes/ispell-resources/fake-aspell.bash > @@ -0,0 +1,2 @@ > +#!/bin/bash > +printf '%s\n' "@(#) International Ispell Version 3.1.20 (but really Aspell 0.59.800)" I asked to use Emacs instead of a Bash script? If that causes problems, let's discuss that, okay? > + (with-temp-buffer > + (let ((default-directory "86e44985-cfba-43ba-98dc-73be46addbc2")) > + (ispell-call-process "emacs" nil t nil '("--batch" "-q" "-nw" "--eval" "(progn (message default-directory) (kill-emacs))")) There's no need for "-nw", "--batch" implies it. Also, I suggest to use "-Q" instead of "-q", so that site-init file is not read. Same comment in the other places you invoke Emacs as a subordinate process. > + (setopt ispell-library-directory "/tmp") Please use temporary-file-directory, not a literal "/tmp" (which is not portable), here and elsewhere. > + (let ((test-dict "nonexistent") > + (test-pdict "/tmp/lnonexistent.txt")) ^^^^^^^^^^^^^^^^^^^^^^^ Likewise here: please use (expand-file-name "lnonexistent.txt" temporary-file-directory) instead. (There are several other places in the patch with the same problem.) From debbugs-submit-bounces@debbugs.gnu.org Thu Aug 07 04:08:13 2025 Received: (at 79177) by debbugs.gnu.org; 7 Aug 2025 08:08:13 +0000 Received: from localhost ([127.0.0.1]:33498 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ujvfX-0006Kx-1m for submit@debbugs.gnu.org; Thu, 07 Aug 2025 04:08:13 -0400 Received: from coconut.lockywolf.net ([2a04:c5c0:0:d7:f816:3eff:fe6b:287f]:34788) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1ujvfS-0006Ke-Hn for 79177@debbugs.gnu.org; Thu, 07 Aug 2025 04:08:09 -0400 Received: from laptop.lockywolf.net (unknown [IPv6:2001:470:24:315::102]) by coconut.lockywolf.net (Postfix) with ESMTPSA id 95CD138633; Thu, 7 Aug 2025 16:08:01 +0800 (CST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=lockywolf.net; s=2024-10-06; t=1754554084; bh=hFOZ5e0DhrX6WZbhK68F52Ne/ciqEUqvNyaKd0rJMak=; h=From:To:Cc:Subject:In-Reply-To:References:Date; b=wQSVYHOulBSw9SOoh4QAADmy9vBgp0cSQr+HQ6xFxn9prjstzx1+MBOhZ616686Oc Y8f3kGuKYXmzD6s+XmAq9XK+JQ9SwIYxlrH3kgTl7Yzh0+DkXlafVjzJMmwFMecfBZ uGEhciI6Kfyw9B2YjvjledC+tf1uEzz+VxOid5UvBapS4e7Ps2vSfNvn9AyRCP0PeF a5P1gV7bavQ6vJRSlU3uuPQgyT7Mb+n6a+ymj9Vz+tRzO78zss/H2YcXosTWNEkA6r lYcJe5Br9Ozomzx5hpULiAYmwzcNdDrFtiXLoNQanx3FNPJOWWAzBFgog6iW4nm7Nh 7MUrru6r3ux5g== From: Lockywolf To: Eli Zaretskii Subject: Re: bug#79177: 31.0.50; [PATCH] [v4] Add tests to ispell.el In-Reply-To: <86v7mzr3bv.fsf@gnu.org> References: <87cy9aj5oi.fsf@laptop.lockywolf.net> <87wm7fvcm0.fsf@laptop.lockywolf.net> <86v7mzr3bv.fsf@gnu.org> User-Agent: mu4e 1.12.9; emacs 31.0.50 Date: Thu, 07 Aug 2025 16:07:54 +0800 Message-ID: <87ikizv9k5.fsf@laptop.lockywolf.net> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Spam-Score: -0.0 (/) X-Debbugs-Envelope-To: 79177 Cc: 79177@debbugs.gnu.org, Lockywolf X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: multipart/signed; boundary="==-=-="; micalg=pgp-sha256; protocol="application/pgp-signature" --==-=-= Content-Type: text/plain Content-Transfer-Encoding: quoted-printable Eli Zaretskii writes: >> Cc: Lockywolf >> From: Lockywolf >> Date: Thu, 07 Aug 2025 15:01:59 +0800 >>=20 >> Updated version of the patch attached: > > Thanks, comments below. > >> >From a5a489689f5361c4a86749ac3c9b33e83b23e528 Mon Sep 17 00:00:00 2001 >> From: Lockywolf >> Date: Thu, 24 Jul 2025 21:07:10 +0800 >> Subject: [PATCH] ispell.el: Add 39 tests. >>=20 >> * test/lisp/textmodes/ispell-tests/*: Add 36 tests for basic utils >> used in ispell.el. >> ispell-program-name >> ispell-with-safe-default-directory >> ispell-call-process >> ispell-create-debug-buffer >> ispell-valid-dictionary-list >> ispell-add-per-file-word-list >> ispell-buffer-local-words >> ispell-init-process >> ispell-buffer-local-dict >> * test/lisp/textmodes/ispell-resources/fake-aspell: Add a mock >> `aspell' for use in ispell.el test. > > Please mention the bug number in the commit log message. > >> diff --git a/test/lisp/textmodes/ispell-resources/fake-aspell.bash b/tes= t/lisp/textmodes/ispell-resources/fake-aspell.bash >> new file mode 100755 >> index 00000000000..4406a18a22e >> --- /dev/null >> +++ b/test/lisp/textmodes/ispell-resources/fake-aspell.bash >> @@ -0,0 +1,2 @@ >> +#!/bin/bash >> +printf '%s\n' "@(#) International Ispell Version 3.1.20 (but really As= pell 0.59.800)" > > I asked to use Emacs instead of a Bash script? If that causes > problems, let's discuss that, okay? Aspell needs to parse the "-vv" command-line argument, and Emacs launched in batch mode cannot do that. Or I did not manage to make it. I am not intentionally ignoring your suggestions, some of my responses got lost while moving from emacs-devel to the bug tracker. > >> + (with-temp-buffer >> + (let ((default-directory "86e44985-cfba-43ba-98dc-73be46addbc2")) >> + (ispell-call-process "emacs" nil t nil '("--batch" "-q" "-nw" "-= -eval" "(progn (message default-directory) (kill-emacs))")) > > There's no need for "-nw", "--batch" implies it. Also, I suggest to > use "-Q" instead of "-q", so that site-init file is not read. > > Same comment in the other places you invoke Emacs as a subordinate > process. Fixed. >> + (setopt ispell-library-directory "/tmp") > > Please use temporary-file-directory, not a literal "/tmp" (which is > not portable), here and elsewhere. Fixed. >> + (let ((test-dict "nonexistent") >> + (test-pdict "/tmp/lnonexistent.txt")) > ^^^^^^^^^^^^^^^^^^^^^^^ > Likewise here: please use > > (expand-file-name "lnonexistent.txt" temporary-file-directory) > > instead. (There are several other places in the patch with the same > problem.) This is not a file path. This is just a string which looks like a file path. If something tries to access this as if it is a path during the execution of the test, this is an error. --==-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iHUEARYIAB0WIQSWBebxCAoqaJP8N/j71sGyD+xtqgUCaJRe3AAKCRD71sGyD+xt qvLOAPwMglJy3WvZHzABPC/dc+bkwsFjSX+WdMQkITPEWLAKdQD/dWqU6HyV2xM+ fdAA5UAUYGAYbYF4e3JCaz5eLaDYQAk= =U8QV -----END PGP SIGNATURE----- --==-=-=-- --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-ispell.el-Add-40-tests.-Fix-79177.patch Content-Description: test-patch >From 9ec0480a0b9b91b60b26d8f1c9427a0cb9e74177 Mon Sep 17 00:00:00 2001 From: Lockywolf Date: Thu, 24 Jul 2025 21:07:10 +0800 Subject: [PATCH] ispell.el: Add 40 tests. Fix 79177 This patch fixes https://debbugs.gnu.org/cgi/bugreport.cgi?bug=79177 * test/lisp/textmodes/ispell-tests/*: Add tests for basic utils used in ispell.el. ispell-program-name ispell-with-safe-default-directory ispell-call-process ispell-create-debug-buffer ispell-valid-dictionary-list ispell-add-per-file-word-list ispell-init-process ispell-buffer-local-words ispell-buffer-local-dict ispell-buffer-local-parsing ispell-accept-buffer-local-defs * test/lisp/textmodes/ispell-resources/fake-aspell: Add a mock `aspell' for use in ispell.el test. --- .../ispell-resources/fake-aspell.bash | 2 + .../textmodes/ispell-tests/ispell-aspell.el | 70 ++ test/lisp/textmodes/ispell-tests/ispell.el | 772 ++++++++++++++++++ 3 files changed, 844 insertions(+) create mode 100755 test/lisp/textmodes/ispell-resources/fake-aspell.bash create mode 100644 test/lisp/textmodes/ispell-tests/ispell-aspell.el create mode 100644 test/lisp/textmodes/ispell-tests/ispell.el diff --git a/test/lisp/textmodes/ispell-resources/fake-aspell.bash b/test/lisp/textmodes/ispell-resources/fake-aspell.bash new file mode 100755 index 00000000000..4406a18a22e --- /dev/null +++ b/test/lisp/textmodes/ispell-resources/fake-aspell.bash @@ -0,0 +1,2 @@ +#!/bin/bash +printf '%s\n' "@(#) International Ispell Version 3.1.20 (but really Aspell 0.59.800)" diff --git a/test/lisp/textmodes/ispell-tests/ispell-aspell.el b/test/lisp/textmodes/ispell-tests/ispell-aspell.el new file mode 100644 index 00000000000..c0886ff53b8 --- /dev/null +++ b/test/lisp/textmodes/ispell-tests/ispell-aspell.el @@ -0,0 +1,70 @@ +;;; tests-ispell-aspell.el --- Test ispell.el aspell backend. -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Lockywolf + +;; Author: Lockywolf +;; Keywords: languages, text + +;; This program 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. + +;; This program 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 this program. If not, see . + +;;; Commentary: + +;; Tests for ispell.el's aspell integration. + +;;; Code: + +(require 'ispell) + +(defvar tests-ispell-data-directory + (expand-file-name "test/lisp/textmodes/ispell-resources/" source-directory)) + +(ert-deftest ispell-aspell-available-ispell-check-version () + "Test that aspell is correctly detected." + (skip-unless (and (executable-find "aspell") + (with-temp-buffer + (call-process "aspell" nil t nil "-vv") + (search-backward "but really Aspell")))) + (should (stringp + (let ((test-saved-ispell-program-name ispell-program-name)) + (unwind-protect + (let () + (setq ispell-last-program-name (time-to-seconds)) + (setf ispell-program-name "aspell") + ispell-really-aspell) + (setf ispell-program-name test-saved-ispell-program-name)))))) + +(ert-deftest ispell-aspell-available-ispell-check-version-error-low () + "Test that aspell is correctly detected." + (let ((fake-aspell-path (expand-file-name + "./fake-aspell.bash" + tests-ispell-data-directory))) + (chmod fake-aspell-path 504) + (call-process fake-aspell-path nil nil nil) + (let ((test-saved-ispell-program-name ispell-program-name) + (test-saved-ispell-last-program-name ispell-last-program-name)) + (unwind-protect + (progn + (setq ispell-last-program-name (time-to-seconds)) + (should-error + (progn + (setopt ispell-program-name fake-aspell-path) + (ispell-check-version t))) + ispell-really-aspell) + (set-variable 'ispell-program-name test-saved-ispell-program-name) + (set-variable 'ispell-last-program-name + test-saved-ispell-last-program-name))))) + + +(provide 'tests-ispell-aspell) +;;; tests-ispell-aspell.el ends here diff --git a/test/lisp/textmodes/ispell-tests/ispell.el b/test/lisp/textmodes/ispell-tests/ispell.el new file mode 100644 index 00000000000..ed4c908faed --- /dev/null +++ b/test/lisp/textmodes/ispell-tests/ispell.el @@ -0,0 +1,772 @@ +;;; tests-ispell.el --- Test ispell.el. -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Lockywolf + +;; Author: Lockywolf +;; Keywords: languages, text + +;; This program 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. + +;; This program 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 this program. If not, see . + +;;; Commentary: + +;; Tests for ispell.el. + +;;; Code: + +(require 'ispell) + +(defun warnings-buffer-exists-p () + "Check if a buffer named \"*Warnings*\" exists." + (if (get-buffer "*Warnings*") + t + nil)) + +(ert-deftest ispell/ispell-program-name/nil () + "Sanity check. Setting a non-string should produce a warning. +Give ispell-program-name a wrong type." + (should (unwind-protect + (progn + (setq ispell-program-name "ispell") + (when (warnings-buffer-exists-p) + (kill-buffer "*Warnings*")) + (setopt ispell-program-name nil) + (if (warnings-buffer-exists-p) + t + nil)) + (when (warnings-buffer-exists-p) + (kill-buffer "*Warnings*"))))) + +(ert-deftest ispell/ispell-program-name/noncommand () + "Sanity check. Should error or at least warn. +Give ispell-program-name a meaningless string." + :expected-result :failed + (should-error + (setopt ispell-program-name "6c628ac4-63a0-11f0-b37c-e38fc166e3fc") ;; random nonexistent name + )) + +(ert-deftest ispell/ispell-program-name/noncommand/interactive () + "Sanity check. Should error or at least warn. +Give ispell-program-name a meaningless string." + (should-error + (progn + (setopt ispell-program-name "6c628ac4-63a0-11f0-b37c-e38fc166e3fc") ;; random nonexistent name + (ispell-check-version) + ))) + +(ert-deftest ispell/ispell-program-name/non-executable () + "Sanity check. Should error or at least warn. +Give ispell-program-name a path to a non-executable. +I personally think that this should always fail, but +at the moment only an interactive call fails." + :expected-result :failed + (should-error + (progn + (setopt ispell-program-name null-device)))) + +(ert-deftest ispell/ispell-program-name/non-executable/interactive () + "Sanity check. Should error or at least warn. +Give ispell-program-name a path to a non-executable." + (should-error + (progn + (setopt ispell-program-name null-device) + (ispell-check-version t)))) + +(ert-deftest ispell/ispell-program-name/non-spellchecker () + "Sanity check. Give ispell-program-name a path to a non-spellchecker. +Fails because for non-interactive runs, `ispell-check-version' does +not actually err." + :expected-result :failed + (skip-unless (executable-find "etags")) + (should-error (string-equal "etags" (setopt ispell-ispell-program "etags")))) + +(ert-deftest ispell/ispell-program-name/non-spellchecker/interactive () + "Sanity check. Give ispell-program-name a path to a non-spellchecker." + (skip-unless (executable-find "etags")) + (should-error + (progn (setopt ispell-ispell-program "etags") + (ispell-check-version t)) + )) + +(ert-deftest ispell/ispell-program-name/ispell () + "Sanity check. If at least some ispell is available, should pass. +Give ispell-program-name a real spellchecker" + (skip-unless (and (executable-find "ispell") + (with-temp-buffer + (call-process "ispell" nil t nil "-vv") + (search-backward "Ispell")))) + ;; should not throw + (should (string-equal "ispell" (setopt ispell-ispell-program "ispell")))) + +(ert-deftest ispell/ispell-with-safe-default-directory/bad () + "Try doing something with a bad default directory." + (should (with-temp-buffer + (let ((default-directory "c296752a-7d7b-4769-a2d4-4bfd96c7ca71")) + (ispell-with-safe-default-directory + (equal default-directory (expand-file-name "~/"))))))) + +(ert-deftest ispell/ispell-with-safe-default-directory/good () + "Try doing something with a bad default directory." + (should (with-temp-buffer + (let ((default-directory temporary-file-directory)) + (ispell-with-safe-default-directory + (equal default-directory temporary-file-directory)))))) + +(ert-deftest ispell/ispell-call-process/simple () + "Check that ispell-call-process works. +This test fails, because HOME is not defined. +This should not be the case, because ispell-call-process +whould be making sure that the directory for running +the backend's process exists." + :expected-result :failed + (should + (with-temp-buffer + (let ((default-directory "86e44985-cfba-43ba-98dc-73be46addbc2")) + (ispell-call-process "emacs" nil t nil '("--batch" "-Q" "--eval" "(progn (message default-directory) (kill-emacs))")) + (search-backward (expand-file-name "~")))))) + +(ert-deftest ispell/ispell-call-process/simple-writable () + "Check that ispell-call-process works." + (should + (with-temp-buffer + (let ((default-directory temporary-file-directory)) + (ispell-call-process "emacs" nil t nil "--batch" "-Q" "--eval" "(message default-directory)") + (search-backward (directory-file-name temporary-file-directory)))))) + +(ert-deftest ispell/ispell-call-process-region/cat-empty () + "Check ispell-call-process-region works with unrelated process. +This test is expected to fail, because at the moment, there is +a construction (let ((default-directory default-directory))...) in +the `ispell-with-safe-default-directory' function, which effectively +makes it useless." + :expected-result :failed + (should + (with-temp-buffer + (let* ((string-to-send "") + (dir (concat temporary-file-directory + "86e44985-cfba-43ba-98dc-73be46addbc2"))) + (make-directory dir t) + (chmod dir 000) + (let ((default-directory dir)) + ;; (ispell-call-process-region string-to-send nil "cat" nil t nil) + (ispell-call-process-region "emacs" nil t nil "--batch" "-Q" "--eval" "(progn (setq this-read (ignore-errors (read-from-minibuffer \"\"))) (message \"%s\" this-read))") + ;; emacs --batch --eval '(progn (setq this-read (ignore-errors (read-from-minibuffer ""))) (message "%s" this-read))' + (equal (buffer-string) string-to-send)))))) + +(ert-deftest ispell/ispell-call-process-region/cat-random () + "Check ispell-call-process-region works with unrelad process. +This test is expected to fail, because at the moment, there is +a construction (let ((default-directory default-directory))...) in +the `ispell-with-safe-default-directory' function, which effectively +makes it useless." + :expected-result :failed + (should + (with-temp-buffer + (let ((string-to-send (format "%s" (random))) + (default-directory "86e44985-cfba-43ba-98dc-73be46addbc2")) + (ispell-call-process-region "emacs" nil t nil "--batch" "-Q" "--eval" "(progn (setq this-read (ignore-errors (read-from-minibuffer \"\"))) (message \"%s\" this-read))") + (equal (buffer-string) string-to-send))))) + +(ert-deftest ispell/ispell-create-debug-buffer () + "Make sure that debug buffer creation works." + (when (bufferp (get-buffer "*ispell-debug*")) + (with-current-buffer "*ispell-debug*" + (rename-buffer "*ispell-debug*-test"))) + (unwind-protect + (progn + (ispell-create-debug-buffer) + (should (bufferp (get-buffer "*ispell-debug*"))) + (kill-buffer "*ispell-debug*") ;; should not error + ) + (when (bufferp (get-buffer "*ispell-debug*-test")) + (with-current-buffer "*ispell-debug*-test" + (rename-buffer "*ispell-debug*")))) + ) + +;; FIXME: this test should probably go into a separate file, dedicated +;; to the hunspell backend, but so far there is not partition between +;; backends, so let us add it here. It is easy to move it. +(ert-deftest ispell/ispell-valid-dictionary-list/hunspell/no-library-directory () + "If hunspell, `ispell-valid-dictionary-list' returns default. +This function only works for aspell and ispell, for hunspell and +enchant-2 it always returns either default or everything. +I think this is an issue in itself, but this test is added to verify +that changes to third-party code do not break existing behaviour." + (skip-unless (executable-find "hunspell")) + (skip-unless (equal 0 (call-process "hunspell" nil nil nil))) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory)) + (unwind-protect + (progn + (setopt ispell-program-name "hunspell") + (setopt ispell-library-directory nil) + (ispell-check-version t) + (should + (equal + (sort (ispell-valid-dictionary-list) 'string<) + (sort (cl-substitute "default" nil (mapcar #'car ispell-dictionary-alist)) 'string<)))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell))) + ) + +;; FIXME: this test should probably go into a separate file, dedicated +;; to the hunspell backend, but so far there is not partition between +;; backends, so let us add it here. It is easy to move it. +(ert-deftest ispell/ispell-valid-dictionary-list/hunspell/library-directory () + "If hunspell, `ispell-valid-dictionary-list' returns default. +This function only works for aspell and ispell, for hunspell and +enchant-2 it always returns either default or everything. +I think this is an issue in itself, but this test is added to verify +that changes to third-party code do not break existing behaviour." + (skip-unless (executable-find "hunspell")) + (skip-unless (equal 0 (call-process "hunspell" nil nil nil))) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory)) + (unwind-protect + (progn + (setopt ispell-program-name "hunspell") + (ispell-check-version t) + (setopt ispell-library-directory temporary-file-directory) + (should + (equal + (ispell-valid-dictionary-list) + '("default")))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell))) + ) + +;; FIXME: this test should probably go into a separate file, dedicated +;; to the enchant-2 backend, but so far there is not partition between +;; backends, so let us add it here. It is easy to move it. +(ert-deftest ispell/ispell-valid-dictionary-list/enchant-2/no-library-directory () + "If enchant-2, `ispell-valid-dictionary-list' returns default. +This function only works for aspell and ispell, for hunspell and +enchant-2 it always returns either default or everything. +I think this is an issue in itself, but this test is added to verify +that changes to third-party code do not break existing behaviour." + (skip-unless (executable-find "enchant-2")) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory)) + (unwind-protect + (progn + (setopt ispell-program-name "enchant-2") + (setopt ispell-library-directory nil) + (ispell-check-version t) + (should + (equal + (sort (ispell-valid-dictionary-list) 'string<) + (sort (cl-substitute "default" nil (mapcar #'car ispell-dictionary-alist)) 'string<)))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell))) + ) + +;; FIXME: this test should probably go into a separate file, dedicated +;; to the enchant-2 backend, but so far there is not partition between +;; backends, so let us add it here. It is easy to move it. +(ert-deftest ispell/ispell-valid-dictionary-list/enchant-2/library-directory () + "If enchant-2, `ispell-valid-dictionary-list' returns default. +This function only works for aspell and ispell, for hunspell and +enchant-2 it always returns either default or everything. +I think this is an issue in itself, but this test is added to verify +that changes to third-party code do not break existing behaviour." + (skip-unless (executable-find "enchant-2")) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory)) + (unwind-protect + (progn + (setopt ispell-program-name "enchant-2") + (setopt ispell-library-directory "/tmp") + (ispell-check-version t) + (should + (equal + (ispell-valid-dictionary-list) + '("default")))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell))) + ) + +(ert-deftest ispell/ispell-valid-dictionary-list/international-ispell () + "Check that ispell-valid-dictionary-list does something useful for ispell. +For ispell, `ispell-valid-dictionary-list' checks that a corresponding +file is present in `ispell-library-directory'." + (skip-unless (executable-find "ispell")) + (skip-unless (let ((libdir (with-temp-buffer + (call-process "ispell" nil t nil "-vv") + (goto-char (point-min)) + (when (re-search-forward + "LIBDIR *= *\"\\([^\"]+\\)\"" nil t) + (match-string 1))))) + (file-readable-p (expand-file-name "english.hash" libdir)))) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory) + (old-ispell-local-dictionary-alist ispell-local-dictionary-alist)) + (unwind-protect + (progn + (setopt ispell-program-name "ispell") ;; this should set ispell-library-directory + (ispell-check-version t) ;; sets ispell-library-directory + (should (not (null ispell-library-directory))) + ;; english is always shipped with international ispell, + ;; other languages not necessarily + (setopt ispell-local-dictionary-alist + '(("english" "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") nil utf-8))) + (should (equal '("english" "default") + (ispell-valid-dictionary-list)))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell) + (setopt ispell-local-dictionary-alist old-ispell-local-dictionary-alist)))) + +(ert-deftest ispell/ispell-valid-dictionary-list/aspell () + "Check that ispell-valid-dictionary-list does something useful for aspell. +For aspell, `ispell-valid-dictionary-list' computes an intersection of +`ispell-dictionary-alist' and `ispell--aspell-found-dictionaries'." + (skip-unless (executable-find "aspell")) + (skip-unless (with-temp-buffer + (call-process "aspell" nil t nil "dicts") + (> (length (buffer-string)) 2))) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory) + (old-ispell-local-dictionary-alist ispell-local-dictionary-alist) + (old-ispell-dictionary-alist ispell-dictionary-alist)) + (unwind-protect + (progn + (setopt ispell-program-name "aspell") ;; this should set ispell-library-directory + (ispell-check-version t) ;; sets ispell-library-directory + ;; english is always shipped with international ispell, + ;; other languages not necessarily + (setopt ispell-local-dictionary-alist + '(("english" "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") nil utf-8))) + (should + (> (length (ispell-valid-dictionary-list)) + (length ispell--aspell-found-dictionaries)))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-dictionary-alist old-ispell-dictionary-alist) + (setopt ispell-program-name old-ispell) + (setopt ispell-local-dictionary-alist old-ispell-local-dictionary-alist)))) + +;; Adding file-local words into the file. (They are _not_ sent to the +;; backend in this function.) + +(ert-deftest ispell/ispell-add-per-file-word-list/simple () + "Adding a per-file word to an empty buffer. No comment +syntax expected." + (with-temp-buffer + (let ((testword (format "%s" (random)))) + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) +(concat " +" ispell-words-keyword " " testword " +")))))) + +(ert-deftest ispell/ispell-add-per-file-word-list/comments () + "Adding a per-file word to an empty buffer. Uses default +emacs-lisp comment syntax." + (with-temp-buffer + (let ((testword (format "%s" (random)))) + (emacs-lisp-mode) + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) +(concat " +; " ispell-words-keyword " " testword " +")))))) + +(ert-deftest ispell/ispell-add-per-file-word-list/nxml () + "Adding a per-file word to an empty buffer. Uses default +xml comment syntax, which has an opening and a closing +marker." + (with-temp-buffer + (let ((testword (format "%s" (random)))) + (nxml-mode) + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) +(concat " + +")))))) + +(ert-deftest ispell/ispell-add-per-file-word-list/keyword-there-space () + "Adding a per-file word to buffer with keyword. Uses default +xml comment syntax, which has an opening and a closing +marker. " + (with-temp-buffer + (let ((testword (format "%s" (random)))) + (nxml-mode) + (insert " + +") + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) +(concat " + +")))))) + +(let* ((backend-binaries (list "ispell" "aspell" "hunspell" "enchant-2")) + (filter-binaries (lambda () + (seq-filter + #'executable-find + backend-binaries)))) + + (defun ispell-tests--some-backend-available-p () + (not + (null (funcall filter-binaries)))) + + (defun ispell-tests--some-backend () + (car (funcall filter-binaries)))) + +(cl-defmacro letopt (bindings &body body) + (declare (indent 1)) + (let* ((binding-var (lambda (binding) (car binding))) + (binding-val (lambda (binding) (cadr binding))) + (make-setopt (lambda (a b) + (list 'setopt a b))) + (vars (seq-map binding-var bindings)) + (values (seq-map binding-val bindings)) + (temp-vars (seq-map #'gensym vars)) + (savebindings (seq-mapn #'list temp-vars vars)) + (tempbindings (seq-mapn make-setopt vars values)) + (restorebindings (seq-mapn make-setopt vars temp-vars))) + `(let ,savebindings + (unwind-protect (progn ,@tempbindings + ,@body) + ,@(reverse restorebindings))))) + +(ert-deftest ispell/ispell-add-per-file-word-list/longline () + "Adding a per-file word to buffer with keyword. Uses default +xml comment syntax, which has an opening and a closing +marker. +This test fails, because ispell.el does not work well with +nXML comments." + :expected-result :failed + (letopt ((ispell-program-name "ispell")) + (with-temp-buffer + (let* ((testword (format "%s" (random))) + (fill-column 50)) + (nxml-mode) + (insert " + +") + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) +(concat " + +" +" + +"))))))) + +;; Adding file-local words from the file's cellar into the backend +;; (@-prefixed, see *man ispell*). (They _are_ sent to the backend in +;; this function.) + +(ert-deftest ispell/ispell-buffer-local-words/ispell-words-keyword () + "Send some words prefixed by @ from the file's cellar to backend. +Should pass regardless of the backend and the dictionary, because +presumably nobody will have `hellooooooo' in their dictionary." + (skip-unless (ispell-tests--some-backend-available-p)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (nxml-mode) + (ignore-errors (ispell-kill-ispell)) + (with-environment-variables (("HOME" temporary-file-directory)) + (ispell-init-process) + (let ((test-output (ispell--run-on-word "hellooooooo"))) + (should (listp test-output)) + (should-not (equal t test-output))) + (ispell-add-per-file-word-list "hellooooooo") + (ispell-buffer-local-words) + (should (equal t (ispell--run-on-word "hellooooooo"))))))) + + +(ert-deftest + ispell/ispell-buffer-local-words/ispell-buffer-session-localwords () + "Send some words prefixed by @ from the file's cellar to backend. +Should pass regardless of the backend and the dictionary, because +presumably nobody will have `hellooooooo' in their dictionary." + (skip-unless (ispell-tests--some-backend-available-p)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (cd temporary-file-directory) + (with-temp-buffer + (nxml-mode) + (ignore-errors (ispell-kill-ispell)) + (with-environment-variables (("HOME" temporary-file-directory)) + (ispell-init-process) + (let ((test-output (ispell--run-on-word "hellooooooo"))) + (should (listp test-output)) + (should-not (equal t test-output))) + (let ((ispell-buffer-session-localwords (list "hellooooooo"))) + (ispell-buffer-local-words) + (should (equal t (ispell--run-on-word "hellooooooo")))))))) + +(ert-deftest ispell/ispell-init-process/works-nohome () + "Simple test to check that ispell-init-process works." + :expected-result :failed + (skip-unless (ispell-tests--some-backend-available-p)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (ispell-init-process)))) + +(ert-deftest ispell/ispell-init-process/works-withhome () + "Simple test to check that ispell-init-process works." + (skip-unless (ispell-tests--some-backend-available-p)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (with-environment-variables (("HOME" temporary-file-directory)) + (ispell-init-process))))) + +;; Some more tests for buffer-local stuff. +;; `ispell-buffer-local-dict' + +(ert-deftest ispell/ispell-buffer-local-dict/no-reload+no-overriden () + "ispell.el can recognise keyword-defined dictionary and keyword-defined +personal-dictionary. With no-reload it needs no backend at all." + (with-temp-buffer + (nxml-mode) + (let ((test-dict "nonexistent") + (test-pdict "/tmp/lnonexistent.txt")) + (insert + "hello\n\n\n" + "" + "") + (ispell-buffer-local-dict t) + (should (equal ispell-local-dictionary test-dict)) + (should (equal ispell-local-pdict test-pdict))))) + +(ert-deftest ispell/ispell-buffer-local-dict/reload+no-overriden () + "ispell.el can recognise keyword-defined dictionary and keyword-defined +personal-dictionary. With no-reload it needs no backend at all." + (with-temp-buffer + (nxml-mode) + (let ((test-dict "nonexistent") + (test-pdict "/tmp/lnonexistent.txt")) + (insert + "hello\n\n\n" + "" + "") + (letopt ((ispell-current-dictionary "nonexistent2")) + (ispell-buffer-local-dict) + (should (equal ispell-current-dictionary test-dict)) + (should (equal ispell-current-personal-dictionary test-pdict)))))) + +(ert-deftest ispell/ispell-buffer-local-dict/no-reload+overriden () + "ispell.el can recognise keyword-defined dictionary and keyword-defined +personal-dictionary. With no-reload it needs no backend at all." + (with-temp-buffer + (nxml-mode) + (let ((test-dict "nonexistent") + (test-pdict "/tmp/lnonexistent.txt")) + (insert + "hello\n\n\n" + "" + "") + (letopt ((ispell-current-dictionary "nonexistent2")) + (let ((ispell-local-dictionary-overridden t)) + (ispell-buffer-local-dict t)) + (should-not (equal ispell-local-dictionary test-dict)) + (should (equal ispell-local-pdict test-pdict)))))) + +(ert-deftest ispell/ispell-buffer-local-dict/reload+overriden () + "ispell.el can recognise keyword-defined dictionary and keyword-defined +personal-dictionary. With no-reload it needs no backend at all." + (with-temp-buffer + (nxml-mode) + (let ((test-dict "nonexistent") + (test-pdict "/tmp/lnonexistent.txt")) + (insert + "hello\n\n\n" + "" + "") + (letopt ((ispell-current-dictionary "nonexistent2")) + (let ((ispell-local-dictionary-overridden t)) + (ispell-buffer-local-dict t)) + (should-not (equal ispell-current-dictionary test-dict)) + (should (equal ispell-current-personal-dictionary test-pdict)))))) + +;; parsing + +(ert-deftest ispell/ispell-buffer-local-parsing/local-keyword () + "Check that ispell.el can suscessfully pick up a tex parser +from a buffer-local keyword." + ;; FIXME: what if default dictionary sets + ;; (ispell-get-extended-character-mode)? + (with-temp-buffer + (let ((test-parser "~tex") + (test-dictname "testdict") + (test-extcharmode "~latin3")) + (letopt ((ispell-parser 'nonexistent) + (ispell-local-dictionary-alist + `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8))) + (ispell-current-dictionary test-dictname)) + + (insert + "hello\n\n\n" ispell-parsing-keyword test-parser) + (let* ((counter 0)) + (cl-labels ((checker (s) + (setq counter (+ 1 counter)) + (when (equal counter 1) + (should (string-equal s "!\n"))) + (when (equal counter 2) + (should (string-equal s "-\n"))) + (when (equal counter 3) + (should (string-equal s (concat test-extcharmode "\n")))) + (when (equal counter 4) + (should (string-equal s (concat test-parser "\n")))) + t)) + (unwind-protect (progn + (advice-add 'ispell-send-string :override + #'checker) + (ispell-buffer-local-parsing)) + (advice-remove 'ispell-send-string #'checker))))))) + ) + +(ert-deftest ispell/ispell-buffer-local-parsing/mode-tex () + "Check that ispell.el can suscessfully pick up a tex parser +from tex-based mode-name. +There is another implicit check here: explicit-character-mode +(argument 7 from the ispell.el dictionary structure) is nil." + (with-temp-buffer + (let ((test-parser "~tex") + (test-dictname "testdict") + (test-extcharmode nil)) + (letopt ((ispell-check-comments t) + (ispell-parser 'use-mode-name) + (ispell-local-dictionary-alist + `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8))) + (ispell-current-dictionary test-dictname)) + (insert + "hello\n\n\n") + (tex-mode) + (let* ((counter 0)) + (cl-labels ((checker (s) + (setq counter (+ 1 counter)) + (when (equal counter 1) + (should (string-equal s "!\n"))) + (when (equal counter 2) + (should (string-equal s "+\n"))) + (when (equal counter 3) + (error "Should not have a third call to `ispell-send-string'")) + t)) + (unwind-protect (progn + (advice-add 'ispell-send-string :override + #'checker) + (ispell-buffer-local-parsing)) + (advice-remove 'ispell-send-string #'checker))))))) + ) + +(ert-deftest ispell/ispell-buffer-local-parsing/extended-character-mode () + "Check that ispell.el can suscessfully pick up an extended character +mode from the dictionary." + (with-temp-buffer + (insert + "hello\n\n\n") + (let ((test-extcharmode "~latin3")) + (letopt ((ispell-check-comments t) + (ispell-parser 'use-mode-name) + ;; FIXME: what if default dictionary sets + ;; (ispell-get-extended-character-mode)? + (ispell-local-dictionary-alist + `(("english" "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8))) + ) + (tex-mode) + (let* ((counter 0)) + (cl-labels ((checker (s) + (setq counter (+ 1 counter)) + (when (equal counter 1) + (should (string-equal s "!\n"))) + (when (equal counter 2) + (should (string-equal s "+\n"))) + (when (equal counter 3) + (should (string-equal s (concat test-extcharmode "\n")))) + (when (equal counter 4) + (error "Should not have a third call to `ispell-send-string'")) + t)) + (unwind-protect (progn + (advice-add 'ispell-send-string :override + #'checker) + (ispell-buffer-local-parsing)) + (advice-remove 'ispell-send-string #'checker))))))) + ) + +;; Let us now test the most important state-related function: +;; `ispell-accept-buffer-local-defs'. +;; Why is it important? +;; Because it is used in emacs' own CI for testing documentation +;; in checkdoc. +;; Indeed, when we are running the checker in batch mode, +;; we do not want to have any global state. + + +(ert-deftest ispell/ispell-accept-buffer-local-defs/simple () + "Check that `ispell-accept-buffer-local-defs' works for a +batch mode. +1. local words +2. dictionary and pdict +3. parser and extcharmode" + (with-environment-variables (("HOME" temporary-file-directory)) + (with-temp-buffer + (let ((test-dictname "english") + (test-extcharmode "~latin3") + (test-parser "~testparser") + (test-localword1 "aaaaaaaaaaaaa") + (test-localword2 "bbbbbbbbbbb") + (test-pdict "test-pdict.pdict")) + (insert + "hello\n\n\n" + ispell-dictionary-keyword test-dictname "\n" + ispell-pdict-keyword (expand-file-name test-pdict temporary-file-directory) "\n" + ispell-parsing-keyword test-parser "\n" + ispell-words-keyword " " test-localword1 " " test-localword2 "\n") + (letopt ((ispell-check-comments t) + (ispell-parser 'tex) + ;; FIXME: what if default dictionary sets + ;; (ispell-get-extended-character-mode)? + (ispell-local-dictionary-alist + `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8)))) + (tex-mode) + (let* ((counter 0)) + (cl-labels ((checker-ispell-send-string (s) + (let ((references + (list nil + (concat test-extcharmode "\n") + (concat "@" test-localword1 "\n") + (concat "@" test-localword2 "\n") + "!\n" + "+\n" + (concat test-extcharmode "\n") + (concat test-parser "\n")))) + (setq counter (+ 1 counter)) + (should (<= counter (length references))) + (should (string-equal + (concat s) + (concat (nth counter references)))) + t))) + (unwind-protect (progn + (advice-add 'ispell-send-string :before + #'checker-ispell-send-string) + (ignore-errors (ispell-kill-ispell)) + (ispell-accept-buffer-local-defs) + (should (equal ispell-local-dictionary test-dictname)) + (should (equal ispell-local-pdict (expand-file-name test-pdict temporary-file-directory))) + ) + (advice-remove 'ispell-send-string #'checker-ispell-send-string)))))))) + ) + +(provide 'tests-ispell) +;;; tests-ispell.el ends here -- 2.46.4 --=-=-= Content-Type: text/plain -- Your sincerely, Vladimir Nikishkin (MiEr, lockywolf) (Laptop) --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Thu Aug 07 05:09:43 2025 Received: (at 79177) by debbugs.gnu.org; 7 Aug 2025 09:09:43 +0000 Received: from localhost ([127.0.0.1]:33623 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ujwd4-0000vY-KM for submit@debbugs.gnu.org; Thu, 07 Aug 2025 05:09:43 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:48196) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1ujwd1-0000vI-7r for 79177@debbugs.gnu.org; Thu, 07 Aug 2025 05:09:40 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ujwcv-0004dC-AF; Thu, 07 Aug 2025 05:09:33 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date: mime-version; bh=57ldkA/O5SdE2IKdEWizY7punkQfL/fKwj6nOO8KAuc=; b=C8jmTNSrjm0V kgIwNBYaM1pYafXVrz1NGq4Ajk389+MPvtpt9DNtBbKMTo2xQTByFJtnQcKSFQ9QuHWeSVrJE7323 IYPdEybLVhGC8k+E3cQNPukbyCxx0ubd25nO9cd15ETXbsrlsBu8Hf+yN7Rhre1SETzz76J/N2rwd BJrsaszfqH3hNg/MywbyPGbxEVPwokRAt25Ke5InKfHOIj0klhncGlP7daELlWb3kj0szKS1FVptq 85Fw1YPIuw3vzhyovfW5rizvKNIqK+W5bLBKDVX3lHOxofYsVlskR6YGhu7tdh9RxCc6XVsl08aWz wnmoEQXgbXU6a+yTwjD/vw==; Date: Thu, 07 Aug 2025 12:09:30 +0300 Message-Id: <86tt2jqz05.fsf@gnu.org> From: Eli Zaretskii To: Lockywolf In-Reply-To: <87ikizv9k5.fsf@laptop.lockywolf.net> (message from Lockywolf on Thu, 07 Aug 2025 16:07:54 +0800) Subject: Re: bug#79177: 31.0.50; [PATCH] [v4] Add tests to ispell.el References: <87cy9aj5oi.fsf@laptop.lockywolf.net> <87wm7fvcm0.fsf@laptop.lockywolf.net> <86v7mzr3bv.fsf@gnu.org> <87ikizv9k5.fsf@laptop.lockywolf.net> X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 79177 Cc: 79177@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Lockywolf > Cc: Lockywolf , 79177@debbugs.gnu.org > Date: Thu, 07 Aug 2025 16:07:54 +0800 > > >> diff --git a/test/lisp/textmodes/ispell-resources/fake-aspell.bash b/test/lisp/textmodes/ispell-resources/fake-aspell.bash > >> new file mode 100755 > >> index 00000000000..4406a18a22e > >> --- /dev/null > >> +++ b/test/lisp/textmodes/ispell-resources/fake-aspell.bash > >> @@ -0,0 +1,2 @@ > >> +#!/bin/bash > >> +printf '%s\n' "@(#) International Ispell Version 3.1.20 (but really Aspell 0.59.800)" > > > > I asked to use Emacs instead of a Bash script? If that causes > > problems, let's discuss that, okay? > > Aspell needs to parse the "-vv" command-line argument, and Emacs > launched in batch mode cannot do that. Or I did not manage to make it. Sorry, I don't understand. The Bash script just outputs a fixed string, and Emacs is capable of doing that. What did you try, and if it didn''t work, how it failed to work? > I am not intentionally ignoring your suggestions, some of my responses > got lost while moving from emacs-devel to the bug tracker. Sorry, I didn't see any explanations, only the new patch, and I responded to what I saw. > >> + (let ((test-dict "nonexistent") > >> + (test-pdict "/tmp/lnonexistent.txt")) > > ^^^^^^^^^^^^^^^^^^^^^^^ > > Likewise here: please use > > > > (expand-file-name "lnonexistent.txt" temporary-file-directory) > > > > instead. (There are several other places in the patch with the same > > problem.) > > This is not a file path. This is just a string which looks like a file > path. If something tries to access this as if it is a path during the > execution of the test, this is an error. OK, but the file name you use is not an absolute file name on Windows, so it could cause problems. Is there a problem with using my suggestion, even though you don't need a real file name? From debbugs-submit-bounces@debbugs.gnu.org Thu Aug 07 05:59:21 2025 Received: (at 79177) by debbugs.gnu.org; 7 Aug 2025 09:59:21 +0000 Received: from localhost ([127.0.0.1]:33726 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ujxP6-0003Md-VB for submit@debbugs.gnu.org; Thu, 07 Aug 2025 05:59:21 -0400 Received: from coconut.lockywolf.net ([213.165.252.157]:33286) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1ujxP4-0003MQ-1W for 79177@debbugs.gnu.org; Thu, 07 Aug 2025 05:59:19 -0400 Received: from laptop.lockywolf.net (unknown [IPv6:2001:470:24:315::102]) by coconut.lockywolf.net (Postfix) with ESMTPSA id 2D10D38659; Thu, 7 Aug 2025 17:59:13 +0800 (CST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=lockywolf.net; s=2024-10-06; t=1754560756; bh=DhdiqChL1xO3wYJ0DZTqQfWrHkWzsGAst9shOTGaXCU=; h=From:To:Cc:Subject:In-Reply-To:References:Date; b=wPDTtAv+3KumWXQXaqb5HJy3Bw61nNQw5A7CG7006Zo6zRRG8O7BzHneP9QVbNfee GT0XK4fsNmYkaLgfVnrTwqpCTOuPXuViq1l+aU3atQZrEuEYVAeYlz3hpw+VzcA31J GXv3Xjp0j+QHktIWyayESwDAex0J/m0YVPL/KK9NOTMo9MIlbOOciwUdqIzzyr2jOf VFnhb2vYX2+3QQbCuLRpx4RUTVO2TLZU6/J3Q9Pasw8Xgu02p/ZfLQT/zow0uSvFy1 KQGVec9KYZk1GkTym+AbsWDYeacZ0vK83j7DGD1zkpBPFkFnYT3Be0VVxtZe93op25 4hjQgiu++IXNw== From: Lockywolf To: Eli Zaretskii Subject: Re: bug#79177: 31.0.50; [PATCH] [v4] Add tests to ispell.el In-Reply-To: <86tt2jqz05.fsf@gnu.org> References: <87cy9aj5oi.fsf@laptop.lockywolf.net> <87wm7fvcm0.fsf@laptop.lockywolf.net> <86v7mzr3bv.fsf@gnu.org> <87ikizv9k5.fsf@laptop.lockywolf.net> <86tt2jqz05.fsf@gnu.org> User-Agent: mu4e 1.12.9; emacs 31.0.50 Date: Thu, 07 Aug 2025 17:59:06 +0800 Message-ID: <87tt2jtpud.fsf@laptop.lockywolf.net> MIME-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha256; protocol="application/pgp-signature" X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 79177 Cc: 79177@debbugs.gnu.org, Lockywolf X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Eli Zaretskii writes: >> From: Lockywolf >> Cc: Lockywolf , 79177@debbugs.gnu.org >> Date: Thu, 07 Aug 2025 16:07:54 +0800 >>=20 >> >> diff --git a/test/lisp/textmodes/ispell-resources/fake-aspell.bash b/= test/lisp/textmodes/ispell-resources/fake-aspell.bash >> >> new file mode 100755 >> >> index 00000000000..4406a18a22e >> >> --- /dev/null >> >> +++ b/test/lisp/textmodes/ispell-resources/fake-aspell.bash >> >> @@ -0,0 +1,2 @@ >> >> +#!/bin/bash >> >> +printf '%s\n' "@(#) International Ispell Version 3.1.20 (but really= Aspell 0.59.800)" >> > >> > I asked to use Emacs instead of a Bash script? If that causes >> > problems, let's discuss that, okay? >>=20 >> Aspell needs to parse the "-vv" command-line argument, and Emacs >> launched in batch mode cannot do that. Or I did not manage to make it. > > Sorry, I don't understand. The Bash script just outputs a fixed > string, and Emacs is capable of doing that. What did you try, and if > it didn''t work, how it failed to work? No, it does not /just/ output a fixed string. It also _ignores_ the command-line argument "-vv", which, if passed to emacs --batch, produces and error Error: error ("Unknown option =E2=80=98-vv=E2=80=99"), and ispell= .el does it when initialising the backend. >> >> + (let ((test-dict "nonexistent") >> >> + (test-pdict "/tmp/lnonexistent.txt")) >> > ^^^^^^^^^^^^^^^^^^^^^^^ >> > Likewise here: please use >> > >> > (expand-file-name "lnonexistent.txt" temporary-file-directory) >> > >> > instead. (There are several other places in the patch with the same >> > problem.) >>=20 >> This is not a file path. This is just a string which looks like a file >> path. If something tries to access this as if it is a path during the >> execution of the test, this is an error. > > OK, but the file name you use is not an absolute file name on Windows, > so it could cause problems. Is there a problem with using my > suggestion, even though you don't need a real file name? ispell's man page says the following: ``` The -p option is used to specify an alternate personal dictionary fi= le. If the file name does not begin with "/", $HOME is prefixed. ``` I do not want ispell to do any mangling of the "HOME" directory if this "string which looks like a path" is by chance (if a test malfunctions) passed to ispell binary and it creates a bogus file in the home directory or overwrites something. On windows "temporary-file-directory" probably does not begin with "/". =2D-=20 Your sincerely, Vladimir Nikishkin (MiEr, lockywolf) (Laptop) --=-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iHUEARYIAB0WIQSWBebxCAoqaJP8N/j71sGyD+xtqgUCaJR47AAKCRD71sGyD+xt qmZ0APwPxy5ZJVUZ6Zz4Q/Nufg+dfwz28t0X/nRGM9YSLOF+qwD5AXAlh6NCAW7k CHNEUf549rWga/7vTtzpM1HDvHX5oAw= =Libc -----END PGP SIGNATURE----- --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Thu Aug 07 06:59:28 2025 Received: (at 79177) by debbugs.gnu.org; 7 Aug 2025 10:59:28 +0000 Received: from localhost ([127.0.0.1]:33798 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ujyLH-0006Bg-V3 for submit@debbugs.gnu.org; Thu, 07 Aug 2025 06:59:28 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:54092) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1ujyLF-0006BS-Od for 79177@debbugs.gnu.org; Thu, 07 Aug 2025 06:59:26 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ujyLA-0000lU-6k; Thu, 07 Aug 2025 06:59:20 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=MIME-version:References:Subject:In-Reply-To:To:From: Date; bh=24CPcH9FHHZ1QV/JG8AGb4ZBjx/RhhKyK1/LGwCJBY0=; b=MDGNmUiSU5e2u7Fry2Cl klFpQFbAPxJ2f7rRhzeZLipSqJmvBYvqE/k/Xelz2RJnRZAuLttIl/KUPUa5EPNeiICHexJBAmP1S 3k4PhIo0GJvP6PT4Vxr+UL2+0oilUZ3fQEqFb0n037+oKxIPwYaM4a/inA8zs43AktCGW5SSvWv/7 5xG3H8Lzy7Dw1uH/1tCE2EcdXcmYZhttPGUAUl8nPjS1jM8+0KLJlwR3aTfPols1rU4ty2wE8q7R5 7MZWM2+YXGaxgywGcmJ8EuUluGbIB79ADXYex/xuQH5HN/isCkJl/YNN+L1/RBH16Ez+TwP/b1HxP 3vbWKEzVNH1OUA==; Date: Thu, 07 Aug 2025 13:59:17 +0300 Message-Id: <86ms8bqtx6.fsf@gnu.org> From: Eli Zaretskii 1From: Eli Zaretskii To: Lockywolf In-Reply-To: <87tt2jtpud.fsf@laptop.lockywolf.net> (message from Lockywolf on Thu, 07 Aug 2025 17:59:06 +0800) Subject: Re: bug#79177: 31.0.50; [PATCH] [v4] Add tests to ispell.el References: <87cy9aj5oi.fsf@laptop.lockywolf.net> <87wm7fvcm0.fsf@laptop.lockywolf.net> <86v7mzr3bv.fsf@gnu.org> <87ikizv9k5.fsf@laptop.lockywolf.net> <86tt2jqz05.fsf@gnu.org> <87tt2jtpud.fsf@laptop.lockywolf.net> MIME-version: 1.0 Content-type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 79177 Cc: 79177@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Lockywolf > Cc: Lockywolf , 79177@debbugs.gnu.org > Date: Thu, 07 Aug 2025 17:59:06 +0800 > > Eli Zaretskii writes: > > >> From: Lockywolf > >> Cc: Lockywolf , 79177@debbugs.gnu.org > >> Date: Thu, 07 Aug 2025 16:07:54 +0800 > >> > >> >> diff --git a/test/lisp/textmodes/ispell-resources/fake-aspell.bash b/test/lisp/textmodes/ispell-resources/fake-aspell.bash > >> >> new file mode 100755 > >> >> index 00000000000..4406a18a22e > >> >> --- /dev/null > >> >> +++ b/test/lisp/textmodes/ispell-resources/fake-aspell.bash > >> >> @@ -0,0 +1,2 @@ > >> >> +#!/bin/bash > >> >> +printf '%s\n' "@(#) International Ispell Version 3.1.20 (but really Aspell 0.59.800)" > >> > > >> > I asked to use Emacs instead of a Bash script? If that causes > >> > problems, let's discuss that, okay? > >> > >> Aspell needs to parse the "-vv" command-line argument, and Emacs > >> launched in batch mode cannot do that. Or I did not manage to make it. > > > > Sorry, I don't understand. The Bash script just outputs a fixed > > string, and Emacs is capable of doing that. What did you try, and if > > it didn''t work, how it failed to work? > > No, it does not /just/ output a fixed string. It also _ignores_ the > command-line argument "-vv", which, if passed to emacs --batch, produces > and error Error: error ("Unknown option ‘-vv’"), and ispell.el does it > when initialising the backend. I feel there's some misunderstanding here. What I suggested is that instead of doing this: (let ((fake-aspell-path (expand-file-name "./fake-aspell.bash" tests-ispell-data-directory))) (chmod fake-aspell-path 504) (call-process fake-aspell-path nil nil nil) where fake-aspell.bash does printf '%s\n' "@(#) International Ispell Version 3.1.20 (but really Aspell 0.59.800)" the code does this (not tested): (call-process "emacs" nil nil nil "-Q" "--batch" "--eval" "(princ \\"@(#) International Ispell Version 3.1.20 (but really Aspell 0.59.800)\\")") For best results, use (expand invocation-name invocation-directory) instead of a literal "emacs". This makes sure you invoke the same Emacs as the one running the test, not some other version. > >> >> + (let ((test-dict "nonexistent") > >> >> + (test-pdict "/tmp/lnonexistent.txt")) > >> > ^^^^^^^^^^^^^^^^^^^^^^^ > >> > Likewise here: please use > >> > > >> > (expand-file-name "lnonexistent.txt" temporary-file-directory) > >> > > >> > instead. (There are several other places in the patch with the same > >> > problem.) > >> > >> This is not a file path. This is just a string which looks like a file > >> path. If something tries to access this as if it is a path during the > >> execution of the test, this is an error. > > > > OK, but the file name you use is not an absolute file name on Windows, > > so it could cause problems. Is there a problem with using my > > suggestion, even though you don't need a real file name? > > ispell's man page says the following: > > ``` > The -p option is used to specify an alternate personal dictionary file. If the file name does not begin with "/", $HOME is prefixed. > ``` > > I do not want ispell to do any mangling of the "HOME" directory if this > "string which looks like a path" is by chance (if a test malfunctions) > passed to ispell binary and it creates a bogus file in the home > directory or overwrites something. > On windows "temporary-file-directory" probably does not begin with "/". Yes, but a Windows port of ispell will know that, and detect absolute file names as appropriate: by looking at the drive letter, as in "d:/foo". On the contrary, using file names that begin with a slash on Windows is likely to fail the test mentioned by the man page. From debbugs-submit-bounces@debbugs.gnu.org Thu Aug 07 22:22:00 2025 Received: (at 79177) by debbugs.gnu.org; 8 Aug 2025 02:22:00 +0000 Received: from localhost ([127.0.0.1]:36453 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ukCk3-0002Bu-9w for submit@debbugs.gnu.org; Thu, 07 Aug 2025 22:22:00 -0400 Received: from coconut.lockywolf.net ([2a04:c5c0:0:d7:f816:3eff:fe6b:287f]:40784) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1ukCjy-0002Bh-JM for 79177@debbugs.gnu.org; Thu, 07 Aug 2025 22:21:56 -0400 Received: from laptop.lockywolf.net (unknown [IPv6:2001:470:24:315::102]) by coconut.lockywolf.net (Postfix) with ESMTPSA id 586B937CD1; Fri, 8 Aug 2025 10:21:48 +0800 (CST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=lockywolf.net; s=2024-10-06; t=1754619712; bh=uhqgoUZP+wE4cSzgr8QBWdbUp040hpmM/WAYxDfjaJA=; h=From:To:Cc:Subject:In-Reply-To:References:Date; b=t+KMhynTFxd6GIS2QukSKdO7yAZH8atrIp5mR8hlu8p3ZV4eP9vQ/9eUrFDcxDRq1 jjTM8a0k6prQDywWPMBO+bkL0C40elFGQ1eP2d1gvmR6tNnSkd+L2GFh/u8aUht0Gy 2Zdd4w8peefT5CZDBnZlgrLZDoTOta7pviXiWXVkyO2Kq+OhCYHVhy/pGCRgKtQW5T yqxvr9DHxv5KjDn4wW6j2az6FtFo51+Xk90ZBd2MggkycMDQxp8FlLVb2FAl3jjdUT jtltZQ7/Xfq0YRxfsgm5hBAhfBqyL2u73dpXyD7gSG1PQD95qy0Xchq1BK0DSRAYyq oKh9+x/r/8kCg== From: Lockywolf To: Eli Zaretskii Subject: Re: bug#79177: 31.0.50; [PATCH] [v4] Add tests to ispell.el In-Reply-To: <86ms8bqtx6.fsf@gnu.org> References: <87cy9aj5oi.fsf@laptop.lockywolf.net> <87wm7fvcm0.fsf@laptop.lockywolf.net> <86v7mzr3bv.fsf@gnu.org> <87ikizv9k5.fsf@laptop.lockywolf.net> <86tt2jqz05.fsf@gnu.org> <87tt2jtpud.fsf@laptop.lockywolf.net> <86ms8bqtx6.fsf@gnu.org> User-Agent: mu4e 1.12.9; emacs 31.0.50 Date: Fri, 08 Aug 2025 10:21:37 +0800 Message-ID: <87sei2sgcu.fsf@laptop.lockywolf.net> MIME-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha256; protocol="application/pgp-signature" X-Spam-Score: -0.0 (/) X-Debbugs-Envelope-To: 79177 Cc: 79177@debbugs.gnu.org, Lockywolf X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Eli Zaretskii writes: >> From: Lockywolf >> Cc: Lockywolf , 79177@debbugs.gnu.org >> Date: Thu, 07 Aug 2025 17:59:06 +0800 >>=20 >> Eli Zaretskii writes: >>=20 >> >> From: Lockywolf >> >> Cc: Lockywolf , 79177@debbugs.gnu.org >> >> Date: Thu, 07 Aug 2025 16:07:54 +0800 >> >>=20 >> >> >> diff --git a/test/lisp/textmodes/ispell-resources/fake-aspell.bash= b/test/lisp/textmodes/ispell-resources/fake-aspell.bash >> >> >> new file mode 100755 >> >> >> index 00000000000..4406a18a22e >> >> >> --- /dev/null >> >> >> +++ b/test/lisp/textmodes/ispell-resources/fake-aspell.bash >> >> >> @@ -0,0 +1,2 @@ >> >> >> +#!/bin/bash >> >> >> +printf '%s\n' "@(#) International Ispell Version 3.1.20 (but rea= lly Aspell 0.59.800)" >> >> > >> >> > I asked to use Emacs instead of a Bash script? If that causes >> >> > problems, let's discuss that, okay? >> >>=20 >> >> Aspell needs to parse the "-vv" command-line argument, and Emacs >> >> launched in batch mode cannot do that. Or I did not manage to make i= t. >> > >> > Sorry, I don't understand. The Bash script just outputs a fixed >> > string, and Emacs is capable of doing that. What did you try, and if >> > it didn''t work, how it failed to work? >>=20 >> No, it does not /just/ output a fixed string. It also _ignores_ the >> command-line argument "-vv", which, if passed to emacs --batch, produces >> and error Error: error ("Unknown option =E2=80=98-vv=E2=80=99"), and isp= ell.el does it >> when initialising the backend. > > I feel there's some misunderstanding here. What I suggested is that > instead of doing this: > > (let ((fake-aspell-path (expand-file-name > "./fake-aspell.bash" > tests-ispell-data-directory))) > (chmod fake-aspell-path 504) > (call-process fake-aspell-path nil nil nil) > > where fake-aspell.bash does > > printf '%s\n' "@(#) International Ispell Version 3.1.20 (but really As= pell 0.59.800)" > > the code does this (not tested): > > (call-process "emacs" nil nil nil "-Q" "--batch" "--eval" "(princ \\"@(= #) International Ispell Version 3.1.20 (but really Aspell 0.59.800)\\")") > > For best results, use > > (expand invocation-name invocation-directory) > > instead of a literal "emacs". This makes sure you invoke the same > Emacs as the one running the test, not some other version. I am not calling "fake_aspell.bash" with "(call-process fake-aspell-path nil nil nil)" in the name of printing a string. I am calling it in order to make sure that calling it works on the target system. Read the test further on, the important lines of the test are ``` (setopt ispell-program-name fake-aspell-path) (ispell-check-version t) ``` where ispell.el will call "fake_aspell.bash" in the way it likes calling backend executables. Maybe I should wrap it in (skip-unless), but I remember it behaving strangely within a "let". >> >> >> + (let ((test-dict "nonexistent") >> >> >> + (test-pdict "/tmp/lnonexistent.txt")) >> >> > ^^^^^^^^^^^^^^^^^^^^^^^ >> >> > Likewise here: please use >> >> > >> >> > (expand-file-name "lnonexistent.txt" temporary-file-directory) >> >> > >> >> > instead. (There are several other places in the patch with the same >> >> > problem.) >> >>=20 >> >> This is not a file path. This is just a string which looks like a fi= le >> >> path. If something tries to access this as if it is a path during the >> >> execution of the test, this is an error. >> > >> > OK, but the file name you use is not an absolute file name on Windows, >> > so it could cause problems. Is there a problem with using my >> > suggestion, even though you don't need a real file name? >>=20 >> ispell's man page says the following: >>=20 >> ``` >> The -p option is used to specify an alternate personal dictionary= file. If the file name does not begin with "/", $HOME is prefixed. >> ``` >>=20 >> I do not want ispell to do any mangling of the "HOME" directory if this >> "string which looks like a path" is by chance (if a test malfunctions) >> passed to ispell binary and it creates a bogus file in the home >> directory or overwrites something. >> On windows "temporary-file-directory" probably does not begin with "/". > > Yes, but a Windows port of ispell will know that, and detect absolute > file names as appropriate: by looking at the drive letter, as in > "d:/foo". On the contrary, using file names that begin with a slash > on Windows is likely to fail the test mentioned by the man page. No, it will not fail. Citing tree.c from ispell's source: ```c /* ** Figure out if p is an absolute path name. Note that beginning ** with "./" and "../" is considered an absolute path, since this ** still means we can't prepend HOME. */ abspath =3D IS_SLASH (*p) || strncmp (p, "./", 2) =3D=3D 0 || strncmp (p, "../", 3) =3D=3D 0; #ifdef MSDOS /* ** DTRT with drive-letter braindamage and with backslashes. */ abspath |=3D strncmp (p, ".\\", 2) =3D=3D 0 || strncmp (p, "..\\", 3) =3D=3D 0 || (p[0] !=3D '\0' && p[1] =3D=3D ':'); #endif ``` That is it, all of it. There is no #ifdef WINDOWS block, and even the MSDOS block only "adds" to the absolute path check, so things starting with "/" are always considered absolute. In fact, quoting the README (line 258) from the ispell root: ``` Although the ispell maintainer does not support MS-DOS and Windows, a generous contributor, Eli Zaretskii, has added MS-DOS support. You can build ispell for MS-DOS with either EMX/GCC or DJGPP. See the file pc/README for compilation instructions. ``` I am surprised that you are asking me this question. =2D-=20 Your sincerely, Vladimir Nikishkin (MiEr, lockywolf) (Laptop) --=-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iHUEARYIAB0WIQSWBebxCAoqaJP8N/j71sGyD+xtqgUCaJVfNQAKCRD71sGyD+xt qgvfAP4+BRNxtEM8FN820lg8xwPO0MuYxK2CxDDqkGqvmx3mlwD/Zd/hhFWIAo6U HxZvnrtyUHr8mCGle6no6sN2tYTzQwU= =cHNb -----END PGP SIGNATURE----- --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Sat Aug 09 06:58:02 2025 Received: (at 79177) by debbugs.gnu.org; 9 Aug 2025 10:58:02 +0000 Received: from localhost ([127.0.0.1]:40384 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ukhGz-00012T-95 for submit@debbugs.gnu.org; Sat, 09 Aug 2025 06:58:02 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:54816) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1ukhGw-000127-5O for 79177@debbugs.gnu.org; Sat, 09 Aug 2025 06:57:59 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ukhGq-0007Gl-H3; Sat, 09 Aug 2025 06:57:52 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=MIME-version:References:Subject:In-Reply-To:To:From: Date; bh=UmwrQY1gonApsi7fbuVVFYIIMg4VC8Vhbuhv9m0NAlU=; b=LDQ6ECkaOlEIlpVgJt72 qwZbZPHYHakpFKV77UFWaeYzifkG+mAD6M5wmsUAdzIcHWcUA8xuDNZwDXrF2BOsGhlwzDZqlsfju 4p7IxrqUjM2mUakLR+q5HLP1gD0O4zBSSNfinMPYyLlBRjz/Kd0peVfBBTbeFUnhujiMC1zNIIxsx uD3o4euZuU4xG2SeZj9X1YFGXdVfR2QVEgVwzdAdi3dWPoipdS+Pfr5qnU3pFBGIfxh6zr9oyX+PE 7tje7zO/eJSzkj/SvNJQMc4MykHu6pZ2AJU3C3gffQVXfRbZ3QVwoOa3/uUkNNuiTn4iQUkLvB0wS E2RWZSBa9mfkdA==; Date: Sat, 09 Aug 2025 13:57:49 +0300 Message-Id: <86a548n4nm.fsf@gnu.org> From: Eli Zaretskii To: Lockywolf In-Reply-To: <87sei2sgcu.fsf@laptop.lockywolf.net> (message from Lockywolf on Fri, 08 Aug 2025 10:21:37 +0800) Subject: Re: bug#79177: 31.0.50; [PATCH] [v4] Add tests to ispell.el References: <87cy9aj5oi.fsf@laptop.lockywolf.net> <87wm7fvcm0.fsf@laptop.lockywolf.net> <86v7mzr3bv.fsf@gnu.org> <87ikizv9k5.fsf@laptop.lockywolf.net> <86tt2jqz05.fsf@gnu.org> <87tt2jtpud.fsf@laptop.lockywolf.net> <86ms8bqtx6.fsf@gnu.org> <87sei2sgcu.fsf@laptop.lockywolf.net> MIME-version: 1.0 Content-type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 79177 Cc: 79177@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Lockywolf > Cc: Lockywolf , 79177@debbugs.gnu.org > Date: Fri, 08 Aug 2025 10:21:37 +0800 > > >> >> Aspell needs to parse the "-vv" command-line argument, and Emacs > >> >> launched in batch mode cannot do that. Or I did not manage to make it. > >> > > >> > Sorry, I don't understand. The Bash script just outputs a fixed > >> > string, and Emacs is capable of doing that. What did you try, and if > >> > it didn''t work, how it failed to work? > >> > >> No, it does not /just/ output a fixed string. It also _ignores_ the > >> command-line argument "-vv", which, if passed to emacs --batch, produces > >> and error Error: error ("Unknown option ‘-vv’"), and ispell.el does it > >> when initialising the backend. > > > > I feel there's some misunderstanding here. What I suggested is that > > instead of doing this: > > > > (let ((fake-aspell-path (expand-file-name > > "./fake-aspell.bash" > > tests-ispell-data-directory))) > > (chmod fake-aspell-path 504) > > (call-process fake-aspell-path nil nil nil) > > > > where fake-aspell.bash does > > > > printf '%s\n' "@(#) International Ispell Version 3.1.20 (but really Aspell 0.59.800)" > > > > the code does this (not tested): > > > > (call-process "emacs" nil nil nil "-Q" "--batch" "--eval" "(princ \\"@(#) International Ispell Version 3.1.20 (but really Aspell 0.59.800)\\")") > > > > For best results, use > > > > (expand invocation-name invocation-directory) > > > > instead of a literal "emacs". This makes sure you invoke the same > > Emacs as the one running the test, not some other version. > > I am not calling "fake_aspell.bash" with "(call-process fake-aspell-path > nil nil nil)" in the name of printing a string. I am calling it in > order to make sure that calling it works on the target system. Read the > test further on, the important lines of the test are > ``` > (setopt ispell-program-name fake-aspell-path) > (ispell-check-version t) > ``` > where ispell.el will call "fake_aspell.bash" in the way it likes calling > backend executables. So this is intended to fail the version check because fake_aspell script ignores the -vv switch? If so, why calling Emacs would not do, since Emacs also doesn't support -vv? > Maybe I should wrap it in (skip-unless), but I remember it behaving > strangely within a "let". I'd prefer not to skip tests if we can avoid that. Relying on shell scripts will require us to do something special on platforms without Bash, and I'd like to avoid that if possible. > >> >> >> + (let ((test-dict "nonexistent") > >> >> >> + (test-pdict "/tmp/lnonexistent.txt")) > >> >> > ^^^^^^^^^^^^^^^^^^^^^^^ > >> >> > Likewise here: please use > >> >> > > >> >> > (expand-file-name "lnonexistent.txt" temporary-file-directory) > >> >> > > >> >> > instead. (There are several other places in the patch with the same > >> >> > problem.) > >> >> > >> >> This is not a file path. This is just a string which looks like a file > >> >> path. If something tries to access this as if it is a path during the > >> >> execution of the test, this is an error. > >> > > >> > OK, but the file name you use is not an absolute file name on Windows, > >> > so it could cause problems. Is there a problem with using my > >> > suggestion, even though you don't need a real file name? > >> > >> ispell's man page says the following: > >> > >> ``` > >> The -p option is used to specify an alternate personal dictionary file. If the file name does not begin with "/", $HOME is prefixed. > >> ``` > >> > >> I do not want ispell to do any mangling of the "HOME" directory if this > >> "string which looks like a path" is by chance (if a test malfunctions) > >> passed to ispell binary and it creates a bogus file in the home > >> directory or overwrites something. > >> On windows "temporary-file-directory" probably does not begin with "/". > > > > Yes, but a Windows port of ispell will know that, and detect absolute > > file names as appropriate: by looking at the drive letter, as in > > "d:/foo". On the contrary, using file names that begin with a slash > > on Windows is likely to fail the test mentioned by the man page. > > No, it will not fail. Citing tree.c from ispell's source: > ```c > /* > ** Figure out if p is an absolute path name. Note that beginning > ** with "./" and "../" is considered an absolute path, since this > ** still means we can't prepend HOME. > */ > abspath = IS_SLASH (*p) || strncmp (p, "./", 2) == 0 > || strncmp (p, "../", 3) == 0; > #ifdef MSDOS > /* > ** DTRT with drive-letter braindamage and with backslashes. > */ > abspath |= strncmp (p, ".\\", 2) == 0 > || strncmp (p, "..\\", 3) == 0 > || (p[0] != '\0' && p[1] == ':'); > #endif > ``` > > That is it, all of it. > There is no #ifdef WINDOWS block, and even the MSDOS block only "adds" > to the absolute path check, so things starting with "/" are always > considered absolute. But it also recognizes the absolute file names that start with a drive letter, which is the full form of an absolute name on Windows. So using expand-file-name will not fail the test, would it? Moreover, we cannot expect all Windows ports of the various spellers to do what Ispell does, and it is IMO wrong to rely on specific details of a specific port in the test suite. What if in some not-too-distant future this test will be also used for Aspell or Hunspell, which might not consider "/foo/bar" and absolute file name on Windows? > In fact, quoting the README (line 258) from the ispell root: > > ``` > Although the ispell maintainer does not support MS-DOS and > Windows, a generous contributor, Eli Zaretskii, has added MS-DOS > support. You can build ispell for MS-DOS with either EMX/GCC or > DJGPP. See the file pc/README for compilation instructions. > ``` > > I am surprised that you are asking me this question. You always remember every line of code you wrote 20+ years ago? And anyway, the important point here is not to rely on such fine details of the implementation, but instead to go with the general conventions of the target platform. Because if the application doesn't behave according to those conventions, it's a bug in the application that needs to be fixed there. Thanks. From debbugs-submit-bounces@debbugs.gnu.org Sat Aug 09 07:49:37 2025 Received: (at 79177) by debbugs.gnu.org; 9 Aug 2025 11:49:37 +0000 Received: from localhost ([127.0.0.1]:40485 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uki4u-0006C5-Qp for submit@debbugs.gnu.org; Sat, 09 Aug 2025 07:49:37 -0400 Received: from coconut.lockywolf.net ([2a04:c5c0:0:d7:f816:3eff:fe6b:287f]:44766) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1uki4s-0006Br-3R for 79177@debbugs.gnu.org; Sat, 09 Aug 2025 07:49:35 -0400 Received: from laptop.lockywolf.net (unknown [IPv6:2001:470:24:315::102]) by coconut.lockywolf.net (Postfix) with ESMTPSA id ABBDF3793E; Sat, 9 Aug 2025 19:49:28 +0800 (CST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=lockywolf.net; s=2024-10-06; t=1754740171; bh=7ylF/gxzfuif93pGKWfTbdyLuoKBfCqWZlAX49C5GQ0=; h=From:To:Cc:Subject:In-Reply-To:References:Date; b=kTF05e5QJLj5hYGxtBSH/iQP86t91ip8f3TAlJEtw/TOcN4G2WD1yoZUcfYkZVRSN clRge3Pqq5x9Z0q7LD9m6x2RMEG4p8B9zILCckQJt2MNfDnB5R6ZHd4aQKtm6E5Cjz 9w3WhYa6rxT21K9iu8VLA/DPfl4E3Mz+bRuI87rV1MEkncZySrmqEy/rIX/q7Lzoc1 mUmKnDN3ciBcqdI+etiC7ONOHsnvyGNie9rTRlIQfbE4T5x3PL6OeTFpItr6tuNsiN yEEblt2jR9Dg9fQblt16lsB3v7CvBHUMx9t6WH+mMRmfuA9D52wLUsIaQBz+JGKMcs qoRgIcGCxAoKw== From: Lockywolf To: Eli Zaretskii Subject: Re: bug#79177: 31.0.50; [PATCH] [v4] Add tests to ispell.el In-Reply-To: <86a548n4nm.fsf@gnu.org> References: <87cy9aj5oi.fsf@laptop.lockywolf.net> <87wm7fvcm0.fsf@laptop.lockywolf.net> <86v7mzr3bv.fsf@gnu.org> <87ikizv9k5.fsf@laptop.lockywolf.net> <86tt2jqz05.fsf@gnu.org> <87tt2jtpud.fsf@laptop.lockywolf.net> <86ms8bqtx6.fsf@gnu.org> <87sei2sgcu.fsf@laptop.lockywolf.net> <86a548n4nm.fsf@gnu.org> User-Agent: mu4e 1.12.9; emacs 31.0.50 Date: Sat, 09 Aug 2025 19:49:19 +0800 Message-ID: <87cy94r9z4.fsf@laptop.lockywolf.net> MIME-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha256; protocol="application/pgp-signature" X-Spam-Score: -0.0 (/) X-Debbugs-Envelope-To: 79177 Cc: 79177@debbugs.gnu.org, Lockywolf X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Eli Zaretskii writes: >> From: Lockywolf >> Cc: Lockywolf , 79177@debbugs.gnu.org >> Date: Fri, 08 Aug 2025 10:21:37 +0800 >>=20 >> >> >> Aspell needs to parse the "-vv" command-line argument, and Emacs >> >> >> launched in batch mode cannot do that. Or I did not manage to mak= e it. >> >> > >> >> > Sorry, I don't understand. The Bash script just outputs a fixed >> >> > string, and Emacs is capable of doing that. What did you try, and = if >> >> > it didn''t work, how it failed to work? >> >>=20 >> >> No, it does not /just/ output a fixed string. It also _ignores_ the >> >> command-line argument "-vv", which, if passed to emacs --batch, produ= ces >> >> and error Error: error ("Unknown option =E2=80=98-vv=E2=80=99"), and = ispell.el does it >> >> when initialising the backend. >> > >> > I feel there's some misunderstanding here. What I suggested is that >> > instead of doing this: >> > >> > (let ((fake-aspell-path (expand-file-name >> > "./fake-aspell.bash" >> > tests-ispell-data-directory))) >> > (chmod fake-aspell-path 504) >> > (call-process fake-aspell-path nil nil nil) >> > >> > where fake-aspell.bash does >> > >> > printf '%s\n' "@(#) International Ispell Version 3.1.20 (but really= Aspell 0.59.800)" >> > >> > the code does this (not tested): >> > >> > (call-process "emacs" nil nil nil "-Q" "--batch" "--eval" "(princ \\= "@(#) International Ispell Version 3.1.20 (but really Aspell 0.59.800)\\")") >> > >> > For best results, use >> > >> > (expand invocation-name invocation-directory) >> > >> > instead of a literal "emacs". This makes sure you invoke the same >> > Emacs as the one running the test, not some other version. >>=20 >> I am not calling "fake_aspell.bash" with "(call-process fake-aspell-path >> nil nil nil)" in the name of printing a string. I am calling it in >> order to make sure that calling it works on the target system. Read the >> test further on, the important lines of the test are >> ``` >> (setopt ispell-program-name fake-aspell-path) >> (ispell-check-version t) >> ``` >> where ispell.el will call "fake_aspell.bash" in the way it likes calling >> backend executables. > > So this is intended to fail the version check because fake_aspell > script ignores the -vv switch? If so, why calling Emacs would not do, > since Emacs also doesn't support -vv? This is intended to succeed, because "fake_aspell.bash", irrespective of the options, behaves in the way real aspell behaves when being called with "-vv". I thought that I might eventually mock the important bits of aspell without actually implementing the dictionary-lookup algorithm, so that it would be possible to run at least some backend-involving tests without having _any_ backend installed on the device. It would have been nice to implement this mock in Emacs, but, as I said, emacs --batch processing of command-line arguments does not allow that at present. I do think that having bash is more common than having ispell, aspell, or hunspell. If you think otherwise, I can replace this call with a call to real {ispell, aspell, hunspell, enchant-2}, like the other tests do. >> Maybe I should wrap it in (skip-unless), but I remember it behaving >> strangely within a "let". > > I'd prefer not to skip tests if we can avoid that. Relying on shell > scripts will require us to do something special on platforms without > Bash, and I'd like to avoid that if possible. I can try to use an :override advice to `call-process' instead. It actually worked on my machine, even though I remember that advising primitive forms is expected to be glitchy. >> >> >> >> + (let ((test-dict "nonexistent") >> >> >> >> + (test-pdict "/tmp/lnonexistent.txt")) >> >> >> > ^^^^^^^^^^^^^^^^^^^^^^^ >> >> >> > Likewise here: please use >> >> >> > >> >> >> > (expand-file-name "lnonexistent.txt" temporary-file-directory) >> >> >> > >> >> >> > instead. (There are several other places in the patch with the = same >> >> >> > problem.) >> >> >>=20 >> >> >> This is not a file path. This is just a string which looks like a= file >> >> >> path. If something tries to access this as if it is a path during= the >> >> >> execution of the test, this is an error. >> >> > >> >> > OK, but the file name you use is not an absolute file name on Windo= ws, >> >> > so it could cause problems. Is there a problem with using my >> >> > suggestion, even though you don't need a real file name? >> >>=20 >> >> ispell's man page says the following: >> >>=20 >> >> ``` >> >> The -p option is used to specify an alternate personal diction= ary file. If the file name does not begin with "/", $HOME is prefixed. >> >> ``` >> >>=20 >> >> I do not want ispell to do any mangling of the "HOME" directory if th= is >> >> "string which looks like a path" is by chance (if a test malfunctions) >> >> passed to ispell binary and it creates a bogus file in the home >> >> directory or overwrites something. >> >> On windows "temporary-file-directory" probably does not begin with "/= ". >> > >> > Yes, but a Windows port of ispell will know that, and detect absolute >> > file names as appropriate: by looking at the drive letter, as in >> > "d:/foo". On the contrary, using file names that begin with a slash >> > on Windows is likely to fail the test mentioned by the man page. >>=20 >> No, it will not fail. Citing tree.c from ispell's source: >> ```c >> /* >> ** Figure out if p is an absolute path name. Note that beginning >> ** with "./" and "../" is considered an absolute path, since this >> ** still means we can't prepend HOME. >> */ >> abspath =3D IS_SLASH (*p) || strncmp (p, "./", 2) =3D=3D 0 >> || strncmp (p, "../", 3) =3D=3D 0; >> #ifdef MSDOS >> /* >> ** DTRT with drive-letter braindamage and with backslashes. >> */ >> abspath |=3D strncmp (p, ".\\", 2) =3D=3D 0 >> || strncmp (p, "..\\", 3) =3D=3D 0 >> || (p[0] !=3D '\0' && p[1] =3D=3D ':'); >> #endif >> ``` >>=20 >> That is it, all of it. >> There is no #ifdef WINDOWS block, and even the MSDOS block only "adds" >> to the absolute path check, so things starting with "/" are always >> considered absolute. > > But it also recognizes the absolute file names that start with a drive > letter, which is the full form of an absolute name on Windows. No it does not. But even if it did... (see the next paragraph)... > So > using expand-file-name will not fail the test, would it? Moreover, we > cannot expect all Windows ports of the various spellers to do what > Ispell does, and it is IMO wrong to rely on specific details of a > specific port in the test suite. What if in some not-too-distant > future this test will be also used for Aspell or Hunspell, which might > not consider "/foo/bar" and absolute file name on Windows? If you look at the test carefully once again, you will see that it does the following #+begin_src emacs-lisp (insert "hello\n\n\n" "" "") #+end_src This test runs the test on a buffer, which is such as if it is written by a user. Surely we can expect files having "Local IspellPersDict: /tmp/nonexistent.dict" in the footer, being _written_ by SOME users, by _opened_on_Windows_ by OTHER users, don't you think? Don't you think that such files are expected to be processed gracefully by Emacs, regardless of whether such paths are absolute, relative, nonexistent, or even UNC? Moreover, a user might even put something like: "Local IspellPersDict: (my-best-personal-dictionary)", or "Local IspellPersDict: https://cgit.git.savannah.gnu.org/cgit/miscfiles.git= /tree/web2" (Such structures are not supported, and should be ignored, or warned about by ~ispell.el~.) This is "user I/O", and tests, at least usually, are not expected to receive graceful input, they are expected to make sure that the code works reasonably even on garbage input. >> In fact, quoting the README (line 258) from the ispell root: >> >> ``` >> Although the ispell maintainer does not support MS-DOS and >> Windows, a generous contributor, Eli Zaretskii, has added MS-DOS >> support. You can build ispell for MS-DOS with either EMX/GCC or >> DJGPP. See the file pc/README for compilation instructions. >> ``` >> >> I am surprised that you are asking me this question. > > You always remember every line of code you wrote 20+ years ago? > And anyway, the important point here is not to rely on such fine > details of the implementation, but instead to go with the general > conventions of the target platform. Because if the application > doesn't behave according to those conventions, it's a bug in the > application that needs to be fixed there. > Fair enough, but overall, the convention of Windows (or any systemn) is to not fail on text files having some UNIX paths in them. I can add a test having "C:\TMP\nonexistent.pdict", to make sure that all platforms are covered, if you'd like. =2D-=20 Your sincerely, Vladimir Nikishkin (MiEr, lockywolf) (Laptop) --=-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iHUEARYIAB0WIQSWBebxCAoqaJP8N/j71sGyD+xtqgUCaJc1wwAKCRD71sGyD+xt qnBjAQDMe/Wqs/gMKci+gd1TIWwsU9Z432g/tm9HtjnJzgbKxwD5AZ4rLYqOqn6w 07acmU/dkdXIwaLb5xzOpIbuZXM2NQk= =qd4C -----END PGP SIGNATURE----- --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Sat Aug 09 09:28:09 2025 Received: (at 79177) by debbugs.gnu.org; 9 Aug 2025 13:28:09 +0000 Received: from localhost ([127.0.0.1]:40884 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ukjcG-0005it-66 for submit@debbugs.gnu.org; Sat, 09 Aug 2025 09:28:08 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:50608) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1ukjcC-0005iR-Fu for 79177@debbugs.gnu.org; Sat, 09 Aug 2025 09:28:06 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ukjc6-00049z-T7; Sat, 09 Aug 2025 09:27:58 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date: mime-version; bh=TOmGRhkzvcLKleH9m7QmzPm6IInec/7ngquWroeujNI=; b=Qujl2x89Hp2p AoEWNisd71ZblfVOKKgEmYinGU8cCe4r5LNE2LSkIARtBMTECtJB2QM9TOA2wFEZSC6WVBycZvbgn hFymLi+z2IadRlKq+CFiCAshLURCy9p7v6OdOqFi50f8UwpJ3Dx/J2fmjd7ouWzl0Kr94NZBTEI+p wuQhgTMz7JRKPzUr0tdyHBAwVoQOQwaqE9FG4sgCJXxpUpXsJG5+4LeOj/jTqTKZC94LP/YutufaE pj6F0SBNvoEO5yBZrXuVSylVAompAmH+syVvhB9S49XSJ1uW3eJr92tn1MpNDoIZYpNPrgNSBMyR7 byj7JAdX20GSIEbBUG/zMQ==; Date: Sat, 09 Aug 2025 16:27:11 +0300 Message-Id: <86ikiwlj68.fsf@gnu.org> From: Eli Zaretskii To: Lockywolf In-Reply-To: <87cy94r9z4.fsf@laptop.lockywolf.net> (message from Lockywolf on Sat, 09 Aug 2025 19:49:19 +0800) Subject: Re: bug#79177: 31.0.50; [PATCH] [v4] Add tests to ispell.el References: <87cy9aj5oi.fsf@laptop.lockywolf.net> <87wm7fvcm0.fsf@laptop.lockywolf.net> <86v7mzr3bv.fsf@gnu.org> <87ikizv9k5.fsf@laptop.lockywolf.net> <86tt2jqz05.fsf@gnu.org> <87tt2jtpud.fsf@laptop.lockywolf.net> <86ms8bqtx6.fsf@gnu.org> <87sei2sgcu.fsf@laptop.lockywolf.net> <86a548n4nm.fsf@gnu.org> <87cy94r9z4.fsf@laptop.lockywolf.net> X-Spam-Score: -0.0 (/) X-Debbugs-Envelope-To: 79177 Cc: 79177@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Lockywolf > Cc: Lockywolf , 79177@debbugs.gnu.org > Date: Sat, 09 Aug 2025 19:49:19 +0800 > > > So this is intended to fail the version check because fake_aspell > > script ignores the -vv switch? If so, why calling Emacs would not do, > > since Emacs also doesn't support -vv? > > This is intended to succeed, because "fake_aspell.bash", irrespective of > the options, behaves in the way real aspell behaves when being called > with "-vv". If there's no way of using some program that we are sure will be installed, please use skip-unless to skip the test when Bash is not available. > >> >> >> >> + (let ((test-dict "nonexistent") > >> >> >> >> + (test-pdict "/tmp/lnonexistent.txt")) > >> >> >> > ^^^^^^^^^^^^^^^^^^^^^^^ > >> >> >> > Likewise here: please use > >> >> >> > > >> >> >> > (expand-file-name "lnonexistent.txt" temporary-file-directory) > >> >> >> > > >> >> >> > instead. (There are several other places in the patch with the same > >> >> >> > problem.) > >> >> >> > >> >> >> This is not a file path. This is just a string which looks like a file > >> >> >> path. If something tries to access this as if it is a path during the > >> >> >> execution of the test, this is an error. > >> >> > > >> >> > OK, but the file name you use is not an absolute file name on Windows, > >> >> > so it could cause problems. Is there a problem with using my > >> >> > suggestion, even though you don't need a real file name? > >> >> > >> >> ispell's man page says the following: > >> >> > >> >> ``` > >> >> The -p option is used to specify an alternate personal dictionary file. If the file name does not begin with "/", $HOME is prefixed. > >> >> ``` > >> >> > >> >> I do not want ispell to do any mangling of the "HOME" directory if this > >> >> "string which looks like a path" is by chance (if a test malfunctions) > >> >> passed to ispell binary and it creates a bogus file in the home > >> >> directory or overwrites something. > >> >> On windows "temporary-file-directory" probably does not begin with "/". > >> > > >> > Yes, but a Windows port of ispell will know that, and detect absolute > >> > file names as appropriate: by looking at the drive letter, as in > >> > "d:/foo". On the contrary, using file names that begin with a slash > >> > on Windows is likely to fail the test mentioned by the man page. > >> > >> No, it will not fail. Citing tree.c from ispell's source: > >> ```c > >> /* > >> ** Figure out if p is an absolute path name. Note that beginning > >> ** with "./" and "../" is considered an absolute path, since this > >> ** still means we can't prepend HOME. > >> */ > >> abspath = IS_SLASH (*p) || strncmp (p, "./", 2) == 0 > >> || strncmp (p, "../", 3) == 0; > >> #ifdef MSDOS > >> /* > >> ** DTRT with drive-letter braindamage and with backslashes. > >> */ > >> abspath |= strncmp (p, ".\\", 2) == 0 > >> || strncmp (p, "..\\", 3) == 0 > >> || (p[0] != '\0' && p[1] == ':'); > >> #endif > >> ``` > >> > >> That is it, all of it. > >> There is no #ifdef WINDOWS block, and even the MSDOS block only "adds" > >> to the absolute path check, so things starting with "/" are always > >> considered absolute. > > > > But it also recognizes the absolute file names that start with a drive > > letter, which is the full form of an absolute name on Windows. > > No it does not. But even if it did... (see the next paragraph)... > > > So > > using expand-file-name will not fail the test, would it? Moreover, we > > cannot expect all Windows ports of the various spellers to do what > > Ispell does, and it is IMO wrong to rely on specific details of a > > specific port in the test suite. What if in some not-too-distant > > future this test will be also used for Aspell or Hunspell, which might > > not consider "/foo/bar" and absolute file name on Windows? > > If you look at the test carefully once again, you will see that it does > the following > #+begin_src emacs-lisp > (insert > "hello\n\n\n" > "" > "") > #+end_src > > This test runs the test on a buffer, which is such as if it is written > by a user. Surely we can expect files having > "Local IspellPersDict: /tmp/nonexistent.dict" in the footer, > being _written_ by SOME users, by _opened_on_Windows_ by OTHER users, > don't you think? Don't you think that such files are expected to be > processed gracefully by Emacs, regardless of whether such paths are > absolute, relative, nonexistent, or even UNC? Moreover, a user might > even put something like: > "Local IspellPersDict: (my-best-personal-dictionary)", > or > "Local IspellPersDict: https://cgit.git.savannah.gnu.org/cgit/miscfiles.git/tree/web2" > (Such structures are not supported, and should be ignored, or warned > about by ~ispell.el~.) > This is "user I/O", and tests, at least usually, are not expected to > receive graceful input, they are expected to make sure that the code > works reasonably even on garbage input. Look, when I see "/foo/bar" file names in Emacs sources, it immediately draws my attention, because such file names cause portability problems. So, unless using expand-file-name and/or temporary-file-directory here is somehow problematic and causes trouble, please do use them, so that this doesn't stand out, okay? > >> In fact, quoting the README (line 258) from the ispell root: > >> > >> ``` > >> Although the ispell maintainer does not support MS-DOS and > >> Windows, a generous contributor, Eli Zaretskii, has added MS-DOS > >> support. You can build ispell for MS-DOS with either EMX/GCC or > >> DJGPP. See the file pc/README for compilation instructions. > >> ``` > >> > >> I am surprised that you are asking me this question. > > > > You always remember every line of code you wrote 20+ years ago? > > > And anyway, the important point here is not to rely on such fine > > details of the implementation, but instead to go with the general > > conventions of the target platform. Because if the application > > doesn't behave according to those conventions, it's a bug in the > > application that needs to be fixed there. > > > > Fair enough, but overall, the convention of Windows (or any systemn) is > to not fail on text files having some UNIX paths in them. I can add a > test having "C:\TMP\nonexistent.pdict", to make sure that all platforms > are covered, if you'd like. See above: I just want these Unix-style absolute file name not to stand out. Thanks. From debbugs-submit-bounces@debbugs.gnu.org Sat Aug 09 11:29:51 2025 Received: (at 79177) by debbugs.gnu.org; 9 Aug 2025 15:29:51 +0000 Received: from localhost ([127.0.0.1]:42777 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uklW2-0003XU-M9 for submit@debbugs.gnu.org; Sat, 09 Aug 2025 11:29:50 -0400 Received: from coconut.lockywolf.net ([213.165.252.157]:43450) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1uklVx-0003XG-Ql for 79177@debbugs.gnu.org; Sat, 09 Aug 2025 11:29:48 -0400 Received: from laptop.lockywolf.net (unknown [IPv6:2001:470:24:315::102]) by coconut.lockywolf.net (Postfix) with ESMTPSA id 6A459385E5; Sat, 9 Aug 2025 23:29:40 +0800 (CST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=lockywolf.net; s=2024-10-06; t=1754753384; bh=dPwa9i91VANg50RjEAI8S/L/gqxvmm1igXE5Q6uEKhA=; h=From:To:Cc:Subject:In-Reply-To:References:Date; b=KlAM8qUAZKrZWbQLg/K1KtBv1n/YIsgbSe32IC9Y3JGZKKA3yw65Wi7rdYt83TKa1 ysE4YDXcfif8H1zXpGhiYjUb4ruhsIUak1MFBFs8xWR0GD1PHlolYKDNxP+OWAibIt NUgYDIWzqgq8/VoA0iu3gqdSsY2nhF+9cSscEbQFmSTX11hV5Z3cECTpLEgEXds+TI 4r4bVNl1/o07/ci+G3lQyMW2a0T4xcjw6SSTxEuHcKj6tpm7j96CB1QPZiDmYbnXb5 k4SeR67WUL0lDHRenffGcDHgnXk9Elkn94Vy9bwnMSPXBT9fXhClN9mf69nhaMW9Ee ZtanlDHB3/Jfg== From: Lockywolf To: Eli Zaretskii Subject: Re: bug#79177: 31.0.50; [PATCH] [v4] Add tests to ispell.el In-Reply-To: <86ikiwlj68.fsf@gnu.org> References: <87cy9aj5oi.fsf@laptop.lockywolf.net> <87wm7fvcm0.fsf@laptop.lockywolf.net> <86v7mzr3bv.fsf@gnu.org> <87ikizv9k5.fsf@laptop.lockywolf.net> <86tt2jqz05.fsf@gnu.org> <87tt2jtpud.fsf@laptop.lockywolf.net> <86ms8bqtx6.fsf@gnu.org> <87sei2sgcu.fsf@laptop.lockywolf.net> <86a548n4nm.fsf@gnu.org> <87cy94r9z4.fsf@laptop.lockywolf.net> <86ikiwlj68.fsf@gnu.org> User-Agent: mu4e 1.12.9; emacs 31.0.50 Date: Sat, 09 Aug 2025 23:29:32 +0800 Message-ID: <874iugqzs3.fsf@laptop.lockywolf.net> MIME-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha256; protocol="application/pgp-signature" X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 79177 Cc: 79177@debbugs.gnu.org, Lockywolf X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain Content-Transfer-Encoding: quoted-printable Eli Zaretskii writes: > If there's no way of using some program that we are sure will be > installed, please use skip-unless to skip the test when Bash is not > available. Done. > Look, when I see "/foo/bar" file names in Emacs sources, it > immediately draws my attention, because such file names cause > portability problems. So, unless using expand-file-name and/or > temporary-file-directory here is somehow problematic and causes > trouble, please do use them, so that this doesn't stand out, okay? > I have tried to make the best of the discussion. The test is there, and it should reliably fail on both GNU and Windows. =2D-=20 Your sincerely, Vladimir Nikishkin (MiEr, lockywolf) (Laptop) --=-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iHUEARYIAB0WIQSWBebxCAoqaJP8N/j71sGyD+xtqgUCaJdpXgAKCRD71sGyD+xt qkf2AQC8wD/AaTxPMfOZgftYfuU7XS4x/rZPrBfucoi+12OanAD+N0kzDdBkvDRL lBAt3PFdd77CKWpItineWjARh+1SvQ4= =7BjX -----END PGP SIGNATURE----- --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Sat Aug 09 11:31:54 2025 Received: (at 79177) by debbugs.gnu.org; 9 Aug 2025 15:31:54 +0000 Received: from localhost ([127.0.0.1]:42788 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uklXz-0003iQ-NS for submit@debbugs.gnu.org; Sat, 09 Aug 2025 11:31:53 -0400 Received: from coconut.lockywolf.net ([2a04:c5c0:0:d7:f816:3eff:fe6b:287f]:36156) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1uklXv-0003iE-CV for 79177@debbugs.gnu.org; Sat, 09 Aug 2025 11:31:49 -0400 Received: from laptop.lockywolf.net (unknown [IPv6:2001:470:24:315::102]) by coconut.lockywolf.net (Postfix) with ESMTPSA id C96C4386EF; Sat, 9 Aug 2025 23:31:38 +0800 (CST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=lockywolf.net; s=2024-10-06; t=1754753506; bh=Dslnp0RtplPaRKlV89sJu1kN80tWfAemUbNSRV35P1s=; h=From:To:Cc:Subject:In-Reply-To:References:Date; b=fZXDCo9nFR+OjghxkLecabuEvZ7fG6J3ahGX4HOZ/JOYH/lYynBeC0aZAxAR3ubuT Eehd2sXA3B+p76ARkrxOjIvQFTbqWuBq3IRA4vHxaobxRESpp+UQ/2bNeXMIQxApm2 POiZRrEUJglxA0/hYZR4HuB8EKnt/jwwsYtkooNuiRpZbQc14yn0KjQWZtegDYItba NgrdWunyAiDvwcXWmKS9GC/uGCM+4CA6531nEM+5xRfS0YYjUEFKZUl2ePAqYJGWdf cD5e4eX7KoWhCpIHJ37cUOvhhW4LDqJZmd6qpharYnneLtuOlXFbXlkW2UN31MLj70 9qmM2r6dFxzww== From: Lockywolf To: Lockywolf Subject: Re: bug#79177: 31.0.50; [PATCH] [v4] Add tests to ispell.el In-Reply-To: <874iugqzs3.fsf@laptop.lockywolf.net> References: <87cy9aj5oi.fsf@laptop.lockywolf.net> <87wm7fvcm0.fsf@laptop.lockywolf.net> <86v7mzr3bv.fsf@gnu.org> <87ikizv9k5.fsf@laptop.lockywolf.net> <86tt2jqz05.fsf@gnu.org> <87tt2jtpud.fsf@laptop.lockywolf.net> <86ms8bqtx6.fsf@gnu.org> <87sei2sgcu.fsf@laptop.lockywolf.net> <86a548n4nm.fsf@gnu.org> <87cy94r9z4.fsf@laptop.lockywolf.net> <86ikiwlj68.fsf@gnu.org> <874iugqzs3.fsf@laptop.lockywolf.net> User-Agent: mu4e 1.12.9; emacs 31.0.50 Date: Sat, 09 Aug 2025 23:31:29 +0800 Message-ID: <87zfc8pl4e.fsf@laptop.lockywolf.net> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Spam-Score: -0.0 (/) X-Debbugs-Envelope-To: 79177 Cc: 79177@debbugs.gnu.org, Eli Zaretskii X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: multipart/signed; boundary="==-=-="; micalg=pgp-sha256; protocol="application/pgp-signature" --==-=-= Content-Type: text/plain Lockywolf writes: > [[PGP Signed Part:Good signature from FBD6C1B20FEC6DAA Lockywolf (Provisional) <*@lockywolf.net> (trust ultimate) created at 2025-08-09T23:29:34+0800 using EDDSA]] > Eli Zaretskii writes: > >> If there's no way of using some program that we are sure will be >> installed, please use skip-unless to skip the test when Bash is not >> available. > > Done. > >> Look, when I see "/foo/bar" file names in Emacs sources, it >> immediately draws my attention, because such file names cause >> portability problems. So, unless using expand-file-name and/or >> temporary-file-directory here is somehow problematic and causes >> trouble, please do use them, so that this doesn't stand out, okay? >> > > I have tried to make the best of the discussion. > The test is there, and it should reliably fail on both GNU and Windows. --==-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iHUEARYIAB0WIQSWBebxCAoqaJP8N/j71sGyD+xtqgUCaJdp0gAKCRD71sGyD+xt qsncAQDL+OI8vLRuJoEcFSsVn9WNyUyAz10Ez6wudQ/SX090YAD/RSwT1+j+8I9L QTnGoJf94RhMeveh6hTRnhUCGOLCnQ8= =LFh3 -----END PGP SIGNATURE----- --==-=-=-- --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-ispell.el-Add-42-tests.-Fix-79177.patch Content-Description: patch >From b4d712a05dfe342c4ce67c399289c1634a5e7905 Mon Sep 17 00:00:00 2001 From: Lockywolf Date: Thu, 24 Jul 2025 21:07:10 +0800 Subject: [PATCH] ispell.el: Add 42 tests. Fix 79177 This patch fixes https://debbugs.gnu.org/cgi/bugreport.cgi?bug=79177 * test/lisp/textmodes/ispell-tests/*: Add tests for basic utils used in ispell.el. ispell-program-name ispell-with-safe-default-directory ispell-call-process ispell-create-debug-buffer ispell-valid-dictionary-list ispell-add-per-file-word-list ispell-init-process ispell-buffer-local-words ispell-buffer-local-dict ispell-buffer-local-parsing ispell-accept-buffer-local-defs * test/lisp/textmodes/ispell-resources/fake-aspell: Add a mock `aspell' for use in ispell.el test. --- .../ispell-resources/fake-aspell.bash | 2 + .../textmodes/ispell-tests/ispell-aspell.el | 74 ++ test/lisp/textmodes/ispell-tests/ispell.el | 862 ++++++++++++++++++ 3 files changed, 938 insertions(+) create mode 100755 test/lisp/textmodes/ispell-resources/fake-aspell.bash create mode 100644 test/lisp/textmodes/ispell-tests/ispell-aspell.el create mode 100644 test/lisp/textmodes/ispell-tests/ispell.el diff --git a/test/lisp/textmodes/ispell-resources/fake-aspell.bash b/test/lisp/textmodes/ispell-resources/fake-aspell.bash new file mode 100755 index 00000000000..4406a18a22e --- /dev/null +++ b/test/lisp/textmodes/ispell-resources/fake-aspell.bash @@ -0,0 +1,2 @@ +#!/bin/bash +printf '%s\n' "@(#) International Ispell Version 3.1.20 (but really Aspell 0.59.800)" diff --git a/test/lisp/textmodes/ispell-tests/ispell-aspell.el b/test/lisp/textmodes/ispell-tests/ispell-aspell.el new file mode 100644 index 00000000000..a3ea07e1eb7 --- /dev/null +++ b/test/lisp/textmodes/ispell-tests/ispell-aspell.el @@ -0,0 +1,74 @@ +;;; tests-ispell-aspell.el --- Test ispell.el aspell backend. -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Lockywolf + +;; Author: Lockywolf +;; Keywords: languages, text + +;; This program 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. + +;; This program 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 this program. If not, see . + +;;; Commentary: + +;; Tests for ispell.el's aspell integration. + +;;; Code: + +(require 'ispell) + +(defvar tests-ispell-data-directory + (expand-file-name "test/lisp/textmodes/ispell-resources/" source-directory)) + +(ert-deftest ispell-aspell-available-ispell-check-version () + "Test that aspell is correctly detected." + (skip-unless (and (executable-find "aspell") + (with-temp-buffer + (call-process "aspell" nil t nil "-vv") + (search-backward "but really Aspell")))) + (should (stringp + (let ((test-saved-ispell-program-name ispell-program-name)) + (unwind-protect + (let () + (setq ispell-last-program-name (time-to-seconds)) + (setf ispell-program-name "aspell") + ispell-really-aspell) + (setf ispell-program-name test-saved-ispell-program-name)))))) + +(ert-deftest ispell-aspell-available-ispell-check-version-error-low () + "Test that aspell is correctly detected." + (skip-unless (progn + (let ((fake-aspell-path (expand-file-name + "./fake-aspell.bash" + tests-ispell-data-directory))) + (chmod fake-aspell-path 504) + (call-process fake-aspell-path nil nil nil)))) + (let ((fake-aspell-path (expand-file-name + "./fake-aspell.bash" + tests-ispell-data-directory))) + (let ((test-saved-ispell-program-name ispell-program-name) + (test-saved-ispell-last-program-name ispell-last-program-name)) + (unwind-protect + (progn + (setq ispell-last-program-name (time-to-seconds)) + (should-error + (progn + (setopt ispell-program-name fake-aspell-path) + (ispell-check-version t))) + ispell-really-aspell) + (set-variable 'ispell-program-name test-saved-ispell-program-name) + (set-variable 'ispell-last-program-name + test-saved-ispell-last-program-name))))) + + +(provide 'tests-ispell-aspell) +;;; tests-ispell-aspell.el ends here diff --git a/test/lisp/textmodes/ispell-tests/ispell.el b/test/lisp/textmodes/ispell-tests/ispell.el new file mode 100644 index 00000000000..ae47017aa64 --- /dev/null +++ b/test/lisp/textmodes/ispell-tests/ispell.el @@ -0,0 +1,862 @@ +;;; tests-ispell.el --- Test ispell.el. -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Lockywolf + +;; Author: Lockywolf +;; Keywords: languages, text + +;; This program 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. + +;; This program 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 this program. If not, see . + +;;; Commentary: + +;; Tests for ispell.el. + +;;; Code: + +(require 'ispell) + +(defun warnings-buffer-exists-p () + "Check if a buffer named \"*Warnings*\" exists." + (if (get-buffer "*Warnings*") + t + nil)) + +(ert-deftest ispell/ispell-program-name/nil () + "Sanity check. Setting a non-string should produce a warning. +Give ispell-program-name a wrong type." + (should (unwind-protect + (progn + (setq ispell-program-name "ispell") + (when (warnings-buffer-exists-p) + (kill-buffer "*Warnings*")) + (setopt ispell-program-name nil) + (if (warnings-buffer-exists-p) + t + nil)) + (when (warnings-buffer-exists-p) + (kill-buffer "*Warnings*"))))) + +(ert-deftest ispell/ispell-program-name/noncommand () + "Sanity check. Should error or at least warn. +Give ispell-program-name a meaningless string." + :expected-result :failed + (should-error + (setopt ispell-program-name "6c628ac4-63a0-11f0-b37c-e38fc166e3fc") ;; random nonexistent name + )) + +(ert-deftest ispell/ispell-program-name/noncommand/interactive () + "Sanity check. Should error or at least warn. +Give ispell-program-name a meaningless string." + (should-error + (progn + (setopt ispell-program-name "6c628ac4-63a0-11f0-b37c-e38fc166e3fc") ;; random nonexistent name + (ispell-check-version) + ))) + +(ert-deftest ispell/ispell-program-name/non-executable () + "Sanity check. Should error or at least warn. +Give ispell-program-name a path to a non-executable. +I personally think that this should always fail, but +at the moment only an interactive call fails." + :expected-result :failed + (should-error + (progn + (setopt ispell-program-name null-device)))) + +(ert-deftest ispell/ispell-program-name/non-executable/interactive () + "Sanity check. Should error or at least warn. +Give ispell-program-name a path to a non-executable." + (should-error + (progn + (setopt ispell-program-name null-device) + (ispell-check-version t)))) + +(ert-deftest ispell/ispell-program-name/non-spellchecker () + "Sanity check. Give ispell-program-name a path to a non-spellchecker. +Fails because for non-interactive runs, `ispell-check-version' does +not actually err." + :expected-result :failed + (skip-unless (executable-find "etags")) + (should-error (string-equal "etags" (setopt ispell-ispell-program "etags")))) + +(ert-deftest ispell/ispell-program-name/non-spellchecker/interactive () + "Sanity check. Give ispell-program-name a path to a non-spellchecker." + (skip-unless (executable-find "etags")) + (should-error + (progn (setopt ispell-ispell-program "etags") + (ispell-check-version t)) + )) + +(ert-deftest ispell/ispell-program-name/ispell () + "Sanity check. If at least some ispell is available, should pass. +Give ispell-program-name a real spellchecker" + (skip-unless (and (executable-find "ispell") + (with-temp-buffer + (call-process "ispell" nil t nil "-vv") + (search-backward "Ispell")))) + ;; should not throw + (should (string-equal "ispell" (setopt ispell-ispell-program "ispell")))) + +(ert-deftest ispell/ispell-with-safe-default-directory/bad () + "Try doing something with a bad default directory." + (should (with-temp-buffer + (let ((default-directory "c296752a-7d7b-4769-a2d4-4bfd96c7ca71")) + (ispell-with-safe-default-directory + (equal default-directory (expand-file-name "~/"))))))) + +(ert-deftest ispell/ispell-with-safe-default-directory/good () + "Try doing something with a bad default directory." + (should (with-temp-buffer + (let ((default-directory temporary-file-directory)) + (ispell-with-safe-default-directory + (equal default-directory temporary-file-directory)))))) + +(ert-deftest ispell/ispell-call-process/simple () + "Check that ispell-call-process works. +This test fails, because HOME is not defined. +This should not be the case, because ispell-call-process +whould be making sure that the directory for running +the backend's process exists." + :expected-result :failed + (should + (with-temp-buffer + (let ((default-directory "86e44985-cfba-43ba-98dc-73be46addbc2")) + (ispell-call-process "emacs" nil t nil '("--batch" "-Q" "--eval" "(progn (message default-directory) (kill-emacs))")) + (search-backward (expand-file-name "~")))))) + +(ert-deftest ispell/ispell-call-process/simple-writable () + "Check that ispell-call-process works." + (should + (with-temp-buffer + (let ((default-directory temporary-file-directory)) + (ispell-call-process "emacs" nil t nil "--batch" "-Q" "--eval" "(message default-directory)") + (search-backward (directory-file-name temporary-file-directory)))))) + +(ert-deftest ispell/ispell-call-process-region/cat-empty () + "Check ispell-call-process-region works with unrelated process. +This test is expected to fail, because at the moment, there is +a construction (let ((default-directory default-directory))...) in +the `ispell-with-safe-default-directory' function, which effectively +makes it useless." + :expected-result :failed + (should + (with-temp-buffer + (let* ((string-to-send "") + (dir (concat temporary-file-directory + "86e44985-cfba-43ba-98dc-73be46addbc2"))) + (make-directory dir t) + (chmod dir 000) + (let ((default-directory dir)) + ;; (ispell-call-process-region string-to-send nil "cat" nil t nil) + (ispell-call-process-region "emacs" nil t nil "--batch" "-Q" "--eval" "(progn (setq this-read (ignore-errors (read-from-minibuffer \"\"))) (message \"%s\" this-read))") + ;; emacs --batch --eval '(progn (setq this-read (ignore-errors (read-from-minibuffer ""))) (message "%s" this-read))' + (equal (buffer-string) string-to-send)))))) + +(ert-deftest ispell/ispell-call-process-region/cat-random () + "Check ispell-call-process-region works with unrelad process. +This test is expected to fail, because at the moment, there is +a construction (let ((default-directory default-directory))...) in +the `ispell-with-safe-default-directory' function, which effectively +makes it useless." + :expected-result :failed + (should + (with-temp-buffer + (let ((string-to-send (format "%s" (random))) + (default-directory "86e44985-cfba-43ba-98dc-73be46addbc2")) + (ispell-call-process-region "emacs" nil t nil "--batch" "-Q" "--eval" "(progn (setq this-read (ignore-errors (read-from-minibuffer \"\"))) (message \"%s\" this-read))") + (equal (buffer-string) string-to-send))))) + +(ert-deftest ispell/ispell-create-debug-buffer () + "Make sure that debug buffer creation works." + (when (bufferp (get-buffer "*ispell-debug*")) + (with-current-buffer "*ispell-debug*" + (rename-buffer "*ispell-debug*-test"))) + (unwind-protect + (progn + (ispell-create-debug-buffer) + (should (bufferp (get-buffer "*ispell-debug*"))) + (kill-buffer "*ispell-debug*") ;; should not error + ) + (when (bufferp (get-buffer "*ispell-debug*-test")) + (with-current-buffer "*ispell-debug*-test" + (rename-buffer "*ispell-debug*")))) + ) + +;; FIXME: this test should probably go into a separate file, dedicated +;; to the hunspell backend, but so far there is not partition between +;; backends, so let us add it here. It is easy to move it. +(ert-deftest ispell/ispell-valid-dictionary-list/hunspell/no-library-directory () + "If hunspell, `ispell-valid-dictionary-list' returns default. +This function only works for aspell and ispell, for hunspell and +enchant-2 it always returns either default or everything. +I think this is an issue in itself, but this test is added to verify +that changes to third-party code do not break existing behaviour." + (skip-unless (executable-find "hunspell")) + (skip-unless (equal 0 (call-process "hunspell" nil nil nil))) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory)) + (unwind-protect + (progn + (setopt ispell-program-name "hunspell") + (setopt ispell-library-directory nil) + (ispell-check-version t) + (should + (equal + (sort (ispell-valid-dictionary-list) 'string<) + (sort (cl-substitute "default" nil (mapcar #'car ispell-dictionary-alist)) 'string<)))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell))) + ) + +;; FIXME: this test should probably go into a separate file, dedicated +;; to the hunspell backend, but so far there is not partition between +;; backends, so let us add it here. It is easy to move it. +(ert-deftest ispell/ispell-valid-dictionary-list/hunspell/library-directory () + "If hunspell, `ispell-valid-dictionary-list' returns default. +This function only works for aspell and ispell, for hunspell and +enchant-2 it always returns either default or everything. +I think this is an issue in itself, but this test is added to verify +that changes to third-party code do not break existing behaviour." + (skip-unless (executable-find "hunspell")) + (skip-unless (equal 0 (call-process "hunspell" nil nil nil))) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory)) + (unwind-protect + (progn + (setopt ispell-program-name "hunspell") + (ispell-check-version t) + (setopt ispell-library-directory temporary-file-directory) + (should + (equal + (ispell-valid-dictionary-list) + '("default")))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell))) + ) + +;; FIXME: this test should probably go into a separate file, dedicated +;; to the enchant-2 backend, but so far there is not partition between +;; backends, so let us add it here. It is easy to move it. +(ert-deftest ispell/ispell-valid-dictionary-list/enchant-2/no-library-directory () + "If enchant-2, `ispell-valid-dictionary-list' returns default. +This function only works for aspell and ispell, for hunspell and +enchant-2 it always returns either default or everything. +I think this is an issue in itself, but this test is added to verify +that changes to third-party code do not break existing behaviour." + (skip-unless (executable-find "enchant-2")) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory)) + (unwind-protect + (progn + (setopt ispell-program-name "enchant-2") + (setopt ispell-library-directory nil) + (ispell-check-version t) + (should + (equal + (sort (ispell-valid-dictionary-list) 'string<) + (sort (cl-substitute "default" nil (mapcar #'car ispell-dictionary-alist)) 'string<)))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell))) + ) + +;; FIXME: this test should probably go into a separate file, dedicated +;; to the enchant-2 backend, but so far there is not partition between +;; backends, so let us add it here. It is easy to move it. +(ert-deftest ispell/ispell-valid-dictionary-list/enchant-2/library-directory () + "If enchant-2, `ispell-valid-dictionary-list' returns default. +This function only works for aspell and ispell, for hunspell and +enchant-2 it always returns either default or everything. +I think this is an issue in itself, but this test is added to verify +that changes to third-party code do not break existing behaviour." + (skip-unless (executable-find "enchant-2")) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory)) + (unwind-protect + (progn + (setopt ispell-program-name "enchant-2") + (setopt ispell-library-directory temporary-file-directory) + (ispell-check-version t) + (should + (equal + (ispell-valid-dictionary-list) + '("default")))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell))) + ) + +(ert-deftest ispell/ispell-valid-dictionary-list/international-ispell () + "Check that ispell-valid-dictionary-list does something useful for ispell. +For ispell, `ispell-valid-dictionary-list' checks that a corresponding +file is present in `ispell-library-directory'." + (skip-unless (executable-find "ispell")) + (skip-unless (let ((libdir (with-temp-buffer + (call-process "ispell" nil t nil "-vv") + (goto-char (point-min)) + (when (re-search-forward + "LIBDIR *= *\"\\([^\"]+\\)\"" nil t) + (match-string 1))))) + (file-readable-p (expand-file-name "english.hash" libdir)))) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory) + (old-ispell-local-dictionary-alist ispell-local-dictionary-alist)) + (unwind-protect + (progn + (setopt ispell-program-name "ispell") ;; this should set ispell-library-directory + (ispell-check-version t) ;; sets ispell-library-directory + (should (not (null ispell-library-directory))) + ;; english is always shipped with international ispell, + ;; other languages not necessarily + (setopt ispell-local-dictionary-alist + '(("english" "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") nil utf-8))) + (should (equal '("english" "default") + (ispell-valid-dictionary-list)))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-program-name old-ispell) + (setopt ispell-local-dictionary-alist old-ispell-local-dictionary-alist)))) + +(ert-deftest ispell/ispell-valid-dictionary-list/aspell () + "Check that ispell-valid-dictionary-list does something useful for aspell. +For aspell, `ispell-valid-dictionary-list' computes an intersection of +`ispell-dictionary-alist' and `ispell--aspell-found-dictionaries'." + (skip-unless (executable-find "aspell")) + (skip-unless (with-temp-buffer + (call-process "aspell" nil t nil "dicts") + (> (length (buffer-string)) 2))) + (let ((old-ispell ispell-program-name) + (old-library-directory ispell-library-directory) + (old-ispell-local-dictionary-alist ispell-local-dictionary-alist) + (old-ispell-dictionary-alist ispell-dictionary-alist)) + (unwind-protect + (progn + (setopt ispell-program-name "aspell") ;; this should set ispell-library-directory + (ispell-check-version t) ;; sets ispell-library-directory + ;; english is always shipped with international ispell, + ;; other languages not necessarily + (setopt ispell-local-dictionary-alist + '(("english" "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") nil utf-8))) + (should + (> (length (ispell-valid-dictionary-list)) + (length ispell--aspell-found-dictionaries)))) + (setopt ispell-library-directory old-library-directory) + (setopt ispell-dictionary-alist old-ispell-dictionary-alist) + (setopt ispell-program-name old-ispell) + (setopt ispell-local-dictionary-alist old-ispell-local-dictionary-alist)))) + +;; Adding file-local words into the file. (They are _not_ sent to the +;; backend in this function.) + +(ert-deftest ispell/ispell-add-per-file-word-list/simple () + "Adding a per-file word to an empty buffer. No comment +syntax expected." + (with-temp-buffer + (let ((testword (format "%s" (random)))) + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) + (concat " +" ispell-words-keyword " " testword " +")))))) + +(ert-deftest ispell/ispell-add-per-file-word-list/comments () + "Adding a per-file word to an empty buffer. Uses default +emacs-lisp comment syntax." + (with-temp-buffer + (let ((testword (format "%s" (random)))) + (emacs-lisp-mode) + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) + (concat " +; " ispell-words-keyword " " testword " +")))))) + +(ert-deftest ispell/ispell-add-per-file-word-list/nxml () + "Adding a per-file word to an empty buffer. Uses default +xml comment syntax, which has an opening and a closing +marker." + (with-temp-buffer + (let ((testword (format "%s" (random)))) + (nxml-mode) + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) + (concat " + +")))))) + +(ert-deftest ispell/ispell-add-per-file-word-list/keyword-there-space () + "Adding a per-file word to buffer with keyword. Uses default +xml comment syntax, which has an opening and a closing +marker. " + (with-temp-buffer + (let ((testword (format "%s" (random)))) + (nxml-mode) + (insert " + +") + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) + (concat " + +")))))) + +(let* ((backend-binaries (list "ispell" "aspell" "hunspell" "enchant-2")) + (filter-binaries (lambda () + (seq-filter + #'executable-find + backend-binaries)))) + + (defun ispell-tests--some-backend-available-p () + (not + (null (funcall filter-binaries)))) + + (defun ispell-tests--some-backend () + (car (funcall filter-binaries)))) + +(cl-defmacro letopt (bindings &body body) + (declare (indent 1)) + (let* ((binding-var (lambda (binding) (car binding))) + (binding-val (lambda (binding) (cadr binding))) + (make-setopt (lambda (a b) + (list 'setopt a b))) + (vars (seq-map binding-var bindings)) + (values (seq-map binding-val bindings)) + (temp-vars (seq-map #'gensym vars)) + (savebindings (seq-mapn #'list temp-vars vars)) + (tempbindings (seq-mapn make-setopt vars values)) + (restorebindings (seq-mapn make-setopt vars temp-vars))) + `(let ,savebindings + (unwind-protect (progn ,@tempbindings + ,@body) + ,@(reverse restorebindings))))) + +(ert-deftest ispell/ispell-add-per-file-word-list/longline () + "Adding a per-file word to buffer with keyword. Uses default +xml comment syntax, which has an opening and a closing +marker. +This test fails, because ispell.el does not work well with +nXML comments." + :expected-result :failed + (letopt ((ispell-program-name "ispell")) + (with-temp-buffer + (let* ((testword (format "%s" (random))) + (fill-column 50)) + (nxml-mode) + (insert " + +") + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) + (concat " + +" +" + +"))))))) + +;; Adding file-local words from the file's cellar into the backend +;; (@-prefixed, see *man ispell*). (They _are_ sent to the backend in +;; this function.) + +(ert-deftest ispell/ispell-buffer-local-words/ispell-words-keyword () + "Send some words prefixed by @ from the file's cellar to backend. +Should pass regardless of the backend and the dictionary, because +presumably nobody will have `hellooooooo' in their dictionary." + (skip-unless (ispell-tests--some-backend-available-p)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (nxml-mode) + (ignore-errors (ispell-kill-ispell)) + (with-environment-variables (("HOME" temporary-file-directory)) + (ispell-init-process) + (let ((test-output (ispell--run-on-word "hellooooooo"))) + (should (listp test-output)) + (should-not (equal t test-output))) + (ispell-add-per-file-word-list "hellooooooo") + (ispell-buffer-local-words) + (should (equal t (ispell--run-on-word "hellooooooo"))))))) + + +(ert-deftest + ispell/ispell-buffer-local-words/ispell-buffer-session-localwords () + "Send some words prefixed by @ from the file's cellar to backend. +Should pass regardless of the backend and the dictionary, because +presumably nobody will have `hellooooooo' in their dictionary." + (skip-unless (ispell-tests--some-backend-available-p)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (cd temporary-file-directory) + (with-temp-buffer + (nxml-mode) + (ignore-errors (ispell-kill-ispell)) + (with-environment-variables (("HOME" temporary-file-directory)) + (ispell-init-process) + (let ((test-output (ispell--run-on-word "hellooooooo"))) + (should (listp test-output)) + (should-not (equal t test-output))) + (let ((ispell-buffer-session-localwords (list "hellooooooo"))) + (ispell-buffer-local-words) + (should (equal t (ispell--run-on-word "hellooooooo")))))))) + +(ert-deftest ispell/ispell-init-process/works-nohome () + "Simple test to check that ispell-init-process works." + :expected-result :failed + (skip-unless (ispell-tests--some-backend-available-p)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (ispell-init-process)))) + +(ert-deftest ispell/ispell-init-process/works-withhome () + "Simple test to check that ispell-init-process works." + (skip-unless (ispell-tests--some-backend-available-p)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (with-environment-variables (("HOME" temporary-file-directory)) + (ispell-init-process))))) + +;; Some more tests for buffer-local stuff. +;; `ispell-buffer-local-dict' +(let ((possible-pdict-paths (list "/tmp/lnonexistent.txt" + "Q:\\nonexistent\\nonexistent.pdict" + "https://example.text" + "(my-favourite-function)" + (format "%s" (random)) + (expand-file-name + (format "%s" (random)) + temporary-file-directory)))) + (ert-deftest ispell/ispell-buffer-local-dict/no-reload+no-overriden () + "ispell.el can recognise keyword-defined dictionary and keyword-defined +personal-dictionary." + (with-temp-buffer + (nxml-mode) + (let ((test-dict "nonexistent")) + (seq-map (lambda (test-pdict) + (insert + "hello\n\n\n" + "" + "") + (ispell-buffer-local-dict t) + (should (equal ispell-local-dictionary test-dict)) + (should (equal ispell-local-pdict test-pdict))) + possible-pdict-paths)))) + + (ert-deftest ispell/ispell-buffer-local-dict/reload+no-overriden () + "ispell.el can recognise keyword-defined dictionary and keyword-defined +personal-dictionary." + :expected-result :failed + (with-temp-buffer + (nxml-mode) + (let ((test-dict "nonexistent")) + (seq-map (lambda (test-pdict) + (insert + "hello\n\n\n" + "" + "") + (letopt ((ispell-current-dictionary "nonexistent2")) + (ispell-buffer-local-dict) + (should (equal ispell-current-dictionary test-dict)) + (should (equal ispell-current-personal-dictionary test-pdict)))) + possible-pdict-paths)))) + + (ert-deftest ispell/ispell-buffer-local-dict/no-reload+overriden () + "ispell.el can recognise keyword-defined dictionary and keyword-defined +personal-dictionary. With no-reload it needs no backend at all." + (with-temp-buffer + (nxml-mode) + (let ((test-dict "nonexistent")) + (seq-map (lambda (test-pdict) + (insert + "hello\n\n\n" + "" + "") + (letopt ((ispell-current-dictionary "nonexistent2")) + (let ((ispell-local-dictionary-overridden t)) + (ispell-buffer-local-dict t)) + (should-not (equal ispell-local-dictionary test-dict)) + (should (equal ispell-local-pdict test-pdict)))) + possible-pdict-paths)))) + + (ert-deftest ispell/ispell-buffer-local-dict/reload+overriden () + "ispell.el can recognise keyword-defined dictionary and keyword-defined +personal-dictionary. With no-reload it needs no backend at all." + :expected-result :failed + (with-temp-buffer + (nxml-mode) + (let ((test-dict "nonexistent")) + (seq-map (lambda (test-pdict) + (insert + "hello\n\n\n" + "" + "") + (letopt ((ispell-current-dictionary "nonexistent2")) + (let ((ispell-local-dictionary-overridden t)) + (ispell-buffer-local-dict t)) + (should-not (equal ispell-current-dictionary test-dict)) + (should (equal ispell-current-personal-dictionary + test-pdict)))) + possible-pdict-paths))))) + +;; parsing + +(ert-deftest ispell/ispell-buffer-local-parsing/local-keyword () + "Check that ispell.el can suscessfully pick up a tex parser +from a buffer-local keyword." + ;; FIXME: what if default dictionary sets + ;; (ispell-get-extended-character-mode)? + (with-temp-buffer + (let ((test-parser "~tex") + (test-dictname "testdict") + (test-extcharmode "~latin3")) + (letopt ((ispell-parser 'nonexistent) + (ispell-local-dictionary-alist + `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8))) + (ispell-current-dictionary test-dictname)) + + (insert + "hello\n\n\n" ispell-parsing-keyword test-parser) + (let* ((counter 0)) + (cl-labels ((checker (s) + (setq counter (+ 1 counter)) + (when (equal counter 1) + (should (string-equal s "!\n"))) + (when (equal counter 2) + (should (string-equal s "-\n"))) + (when (equal counter 3) + (should (string-equal s (concat test-extcharmode "\n")))) + (when (equal counter 4) + (should (string-equal s (concat test-parser "\n")))) + t)) + (unwind-protect (progn + (advice-add 'ispell-send-string :override + #'checker) + (ispell-buffer-local-parsing)) + (advice-remove 'ispell-send-string #'checker))))))) + ) + +(ert-deftest ispell/ispell-buffer-local-parsing/mode-tex () + "Check that ispell.el can suscessfully pick up a tex parser +from tex-based mode-name. +There is another implicit check here: explicit-character-mode +(argument 7 from the ispell.el dictionary structure) is nil." + (with-temp-buffer + (let ((test-dictname "testdict") + (test-extcharmode nil)) + (letopt ((ispell-check-comments t) + (ispell-parser 'use-mode-name) + (ispell-local-dictionary-alist + `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8))) + (ispell-current-dictionary test-dictname)) + (insert + "hello\n\n\n") + (tex-mode) + (let* ((counter 0)) + (cl-labels ((checker (s) + (setq counter (+ 1 counter)) + (when (equal counter 1) + (should (string-equal s "!\n"))) + (when (equal counter 2) + (should (string-equal s "+\n"))) + (when (equal counter 3) + (error "Should not have a third call to `ispell-send-string'")) + t)) + (unwind-protect (progn + (advice-add 'ispell-send-string :override + #'checker) + (ispell-buffer-local-parsing)) + (advice-remove 'ispell-send-string #'checker))))))) + ) + +(ert-deftest ispell/ispell-buffer-local-parsing/extended-character-mode () + "Check that ispell.el can suscessfully pick up an extended character +mode from the dictionary." + (with-temp-buffer + (insert + "hello\n\n\n") + (let ((test-extcharmode "~latin3")) + (letopt ((ispell-check-comments t) + (ispell-parser 'use-mode-name) + ;; FIXME: what if default dictionary sets + ;; (ispell-get-extended-character-mode)? + (ispell-local-dictionary-alist + `(("english" "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8))) + ) + (tex-mode) + (let* ((counter 0)) + (cl-labels ((checker (s) + (setq counter (+ 1 counter)) + (when (equal counter 1) + (should (string-equal s "!\n"))) + (when (equal counter 2) + (should (string-equal s "+\n"))) + (when (equal counter 3) + (should (string-equal s (concat test-extcharmode "\n")))) + (when (equal counter 4) + (error "Should not have a third call to `ispell-send-string'")) + t)) + (unwind-protect (progn + (advice-add 'ispell-send-string :override + #'checker) + (ispell-buffer-local-parsing)) + (advice-remove 'ispell-send-string #'checker))))))) + ) + +;; Let us now test the most important state-related function: +;; `ispell-accept-buffer-local-defs'. +;; Why is it important? +;; Because it is used in emacs' own CI for testing documentation +;; in checkdoc. +;; Indeed, when we are running the checker in batch mode, +;; we do not want to have any global state. + + +(ert-deftest ispell/ispell-accept-buffer-local-defs/simple () + "Check that `ispell-accept-buffer-local-defs' works for a +batch mode. +1. local words +2. dictionary and pdict +3. parser and extcharmode" + (with-environment-variables (("HOME" temporary-file-directory)) + (with-temp-buffer + (let ((test-dictname "english") + (test-extcharmode "~latin3") + (test-parser "~testparser") + (test-localword1 "aaaaaaaaaaaaa") + (test-localword2 "bbbbbbbbbbb") + (test-pdict "test-pdict.pdict")) + (insert + "hello\n\n\n" + ispell-dictionary-keyword test-dictname "\n" + ispell-pdict-keyword (expand-file-name test-pdict temporary-file-directory) "\n" + ispell-parsing-keyword test-parser "\n" + ispell-words-keyword " " test-localword1 " " test-localword2 "\n") + (letopt ((ispell-check-comments t) + (ispell-parser 'tex) + ;; FIXME: what if default dictionary sets + ;; (ispell-get-extended-character-mode)? + (ispell-local-dictionary-alist + `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8)))) + (tex-mode) + (let* ((counter 0)) + (cl-labels ((checker-ispell-send-string (s) + (let ((references + (list nil + (concat test-extcharmode "\n") + (concat "@" test-localword1 "\n") + (concat "@" test-localword2 "\n") + "!\n" + "+\n" + (concat test-extcharmode "\n") + (concat test-parser "\n")))) + (setq counter (+ 1 counter)) + (should (<= counter (length references))) + (should (string-equal + (concat s) + (concat (nth counter references)))) + t))) + (unwind-protect (progn + (advice-add 'ispell-send-string :before + #'checker-ispell-send-string) + (ignore-errors (ispell-kill-ispell)) + (ispell-accept-buffer-local-defs) + (should (equal ispell-local-dictionary test-dictname)) + (should (equal ispell-local-pdict (expand-file-name test-pdict temporary-file-directory))) + ) + (advice-remove 'ispell-send-string #'checker-ispell-send-string)))))))) + ) + +(ert-deftest ispell/ispell--run-on-word/default () + "`ispell--run-on-word' should be the simplest interface +for checking a word." + (skip-unless (ispell-tests--some-backend-available-p)) + (skip-unless (equal + 0 + (call-process (ispell-tests--some-backend) nil nil nil "-vv"))) + (letopt ((ispell-program-name (ispell-tests--some-backend)) + (ispell-dictionary "default")) + (let ((default-directory temporary-file-directory)) + (with-temp-buffer + (with-environment-variables (("HOME" temporary-file-directory)) + (nxml-mode) + ;; t t kills regardless and clears buffer-local words + (ignore-errors (ispell-kill-ispell t t)) + (ispell-init-process) + + (let ((test-output (ispell--run-on-word "hellooooooo"))) + (should (listp test-output)) + (should-not (equal t test-output)) + (setq ispell-filter nil) + (setq ispell-filter-continue nil)) + + (let ((test-output (ispell--run-on-word "hello"))) + (should-not (listp test-output)) + (should (equal t test-output)) + (setq ispell-filter nil) + (setq ispell-filter-continue nil)) + + (let ((test-output (ispell--run-on-word "fail"))) + (should-not (listp test-output)) + (should (equal t test-output)) + (setq ispell-filter nil) + (setq ispell-filter-continue nil)) + + (let ((test-output (ispell--run-on-word "tail"))) + (should-not (listp test-output)) + (should (equal t test-output)) + (setq ispell-filter nil) + (setq ispell-filter-continue nil)) + )))) + ) + +(ert-deftest ispell/ispell--run-on-word/default/fails () + "`ispell--run-on-word' should be the simplest interface +for checking a word. This test fails due to what I consider +to be a bug. I am quite convinced that `ispell--run-on-word' +should work twice in a row, without having to call +(`ispell-init-process') or (setq ispell-filter nil) +before each call. +" + :expected-result :failed + (skip-unless (ispell-tests--some-backend-available-p)) + (skip-unless (equal + 0 + (call-process (ispell-tests--some-backend) nil nil nil "-vv"))) + (letopt ((ispell-program-name (ispell-tests--some-backend)) + (ispell-dictionary "default")) + (let ((default-directory temporary-file-directory)) + (with-temp-buffer + (with-environment-variables (("HOME" temporary-file-directory)) + (nxml-mode) + ;; t t kills regardless and clears buffer-local words + (ignore-errors (ispell-kill-ispell t t)) + (ispell-init-process) + + (let ((test-output (ispell--run-on-word "hellooooooo"))) + (should (listp test-output)) + (should-not (equal t test-output))) + + (let ((test-output (ispell--run-on-word "hello"))) + (should-not (listp test-output)) + (should (equal t test-output))) + + )))) + ) + +(provide 'tests-ispell) +;;; tests-ispell.el ends here -- 2.46.4 --=-=-= Content-Type: text/plain -- Your sincerely, Vladimir Nikishkin (MiEr, lockywolf) (Laptop) --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Sat Aug 09 12:08:32 2025 Received: (at 79177) by debbugs.gnu.org; 9 Aug 2025 16:08:32 +0000 Received: from localhost ([127.0.0.1]:42855 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ukm7T-0005Kz-K5 for submit@debbugs.gnu.org; Sat, 09 Aug 2025 12:08:32 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:44558) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1ukm7P-0005Kh-58 for 79177@debbugs.gnu.org; Sat, 09 Aug 2025 12:08:30 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ukm7J-0007Pg-MT; Sat, 09 Aug 2025 12:08:21 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date: mime-version; bh=AAvmuC7d38n0dNc350k3mOLed8GTEpkhsBomOnu5XLs=; b=LHEp9JifpWcw bMhFkgaW01acfxMtuI2x4Aqr+FMSfBuJroI1e4imxQDu97HYGSrYc8dg4kn2Rtd1wlHPl5+1nkdsn X3/AmZ6myPvwG6utRY2JAwyVYQn1hCcVWgm2s1axE1WD940mjbfaZtVuPotHKKhxzrme6ia1YN2Gv iQuqA/hYIDC2VLh/+TBMqI2VLtN7do7Q1NrABwveGUmStUXvG8kJ4ILOQNUkLPNNQd8Lc/L5ilfPy Gvr+eUK8uwjaaVx2x7ca9VbO5zLtS0rjmnZd5GNT4wNatxZcGGqIXP7AAhwquvTl87pAi/hrk4uC+ v4m3vl6+KQa8fK5Vaq7sYQ==; Date: Sat, 09 Aug 2025 19:08:18 +0300 Message-Id: <867bzclbpp.fsf@gnu.org> From: Eli Zaretskii To: Lockywolf In-Reply-To: <87zfc8pl4e.fsf@laptop.lockywolf.net> (message from Lockywolf on Sat, 09 Aug 2025 23:31:29 +0800) Subject: Re: bug#79177: 31.0.50; [PATCH] [v4] Add tests to ispell.el References: <87cy9aj5oi.fsf@laptop.lockywolf.net> <87wm7fvcm0.fsf@laptop.lockywolf.net> <86v7mzr3bv.fsf@gnu.org> <87ikizv9k5.fsf@laptop.lockywolf.net> <86tt2jqz05.fsf@gnu.org> <87tt2jtpud.fsf@laptop.lockywolf.net> <86ms8bqtx6.fsf@gnu.org> <87sei2sgcu.fsf@laptop.lockywolf.net> <86a548n4nm.fsf@gnu.org> <87cy94r9z4.fsf@laptop.lockywolf.net> <86ikiwlj68.fsf@gnu.org> <874iugqzs3.fsf@laptop.lockywolf.net> <87zfc8pl4e.fsf@laptop.lockywolf.net> X-Spam-Score: -0.0 (/) X-Debbugs-Envelope-To: 79177 Cc: 79177@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Lockywolf > Cc: Eli Zaretskii , 79177@debbugs.gnu.org > Date: Sat, 09 Aug 2025 23:31:29 +0800 > > Lockywolf writes: > > > [[PGP Signed Part:Good signature from FBD6C1B20FEC6DAA Lockywolf (Provisional) <*@lockywolf.net> (trust ultimate) created at 2025-08-09T23:29:34+0800 using EDDSA]] > > Eli Zaretskii writes: > > > >> If there's no way of using some program that we are sure will be > >> installed, please use skip-unless to skip the test when Bash is not > >> available. > > > > Done. > > > >> Look, when I see "/foo/bar" file names in Emacs sources, it > >> immediately draws my attention, because such file names cause > >> portability problems. So, unless using expand-file-name and/or > >> temporary-file-directory here is somehow problematic and causes > >> trouble, please do use them, so that this doesn't stand out, okay? > >> > > > > I have tried to make the best of the discussion. > > The test is there, and it should reliably fail on both GNU and Windows. Thanks. I've now run the tests on my system, and I have a couple of minor comments. First, the names of the test files don't follow our conventions. They should be ispell-aspell-tests.el and ispell-ispell-tests.el or just ispell-tests.el. IOW, the names must end in "-tests.el". > +(ert-deftest ispell/ispell-init-process/works-nohome () > + "Simple test to check that ispell-init-process works." > + :expected-result :failed > + (skip-unless (ispell-tests--some-backend-available-p)) > + (letopt ((ispell-program-name (ispell-tests--some-backend))) > + (with-temp-buffer > + (ispell-init-process)))) This test unexpectedly passed on my system. Can you explain why you expected it to fail? > +(ert-deftest ispell/ispell-accept-buffer-local-defs/simple () > + "Check that `ispell-accept-buffer-local-defs' works for a > +batch mode. > +1. local words > +2. dictionary and pdict > +3. parser and extcharmode" > + (with-environment-variables (("HOME" temporary-file-directory)) > + (with-temp-buffer > + (let ((test-dictname "english") > + (test-extcharmode "~latin3") > + (test-parser "~testparser") > + (test-localword1 "aaaaaaaaaaaaa") > + (test-localword2 "bbbbbbbbbbb") > + (test-pdict "test-pdict.pdict")) This test is Ispell-specific, it will not work if the spell-checker is, say, Hunspell. For example, Hunspell's English dictionary is "en_US", not "English". So you should skip it if the backend is not literally Ispell.