mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-04-03 04:41:23 -07:00
* test/lisp/textmodes/ispell-tests/ispell-tests.el (ispell/ispell-accept-buffer-local-defs/simple): Don't treat Aspell as Ispell even if it pretends to be. (Bug#80165)
1556 lines
64 KiB
EmacsLisp
1556 lines
64 KiB
EmacsLisp
;;; tests-ispell.el --- Test ispell.el. -*- lexical-binding: t; -*-
|
|
|
|
;; Copyright (C) 2025 Lockywolf
|
|
|
|
;; Author: Lockywolf <for_emacs_1@lockywolf.net>
|
|
;; 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 <https://www.gnu.org/licenses/>.
|
|
|
|
;;; Commentary:
|
|
|
|
;; Tests for ispell.el.
|
|
|
|
;;; Code:
|
|
|
|
(require 'ert)
|
|
(require 'ert-x)
|
|
(require 'ispell)
|
|
|
|
(eval-and-compile
|
|
(let ((load-path (cons (file-name-directory
|
|
(or (macroexp-file-name) load-file-name))
|
|
load-path)))
|
|
(require 'ispell-tests-common)))
|
|
|
|
(defconst ispell-tests-emacs-binary-path
|
|
(concat invocation-directory invocation-name))
|
|
|
|
(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 ()
|
|
"Should error or at least warn when `ispell-program-name' is a meaningless string."
|
|
:expected-result :failed
|
|
(should-error
|
|
(setopt ispell-program-name "6c628ac4-63a0-11f0-b37c-e38fc166e3fc") ;; random ispellnonexistent name
|
|
))
|
|
|
|
(ert-deftest ispell/ispell-program-name/noncommand/interactive ()
|
|
"Should error or at least warn when `ispell-program-name' is a meaningless string."
|
|
(should-error
|
|
(progn
|
|
(setopt ispell-program-name "baf8cc1a-a811-11f0-abc7-ef02b7a6dda1") ;; random ispellnonexistent 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 the interactive call fails."
|
|
:expected-result :failed
|
|
(should-error
|
|
(progn
|
|
(setopt ispell-program-name null-device))))
|
|
|
|
(ert-deftest ispell/ispell-program-name/non-executable/interactive ()
|
|
"Should error or at least warn if `ispell-program-name' is 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 ()
|
|
"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 ()
|
|
"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'
|
|
would 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 ispell-tests-emacs-binary-path 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
|
|
ispell-tests-emacs-binary-path nil t nil "--batch" "-Q"
|
|
"--eval" "(message \"%s\" (expand-file-name 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 ispell-tests-emacs-binary-path 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 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 (format "%s" (random)))
|
|
(default-directory "86e44985-cfba-43ba-98dc-73be46addbc2"))
|
|
(ispell-call-process-region ispell-tests-emacs-binary-path 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 behavior."
|
|
(skip-unless (executable-find "hunspell"))
|
|
(skip-unless (equal 0 (call-process "hunspell" nil nil nil)))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name "hunspell")
|
|
(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<))))
|
|
)
|
|
|
|
;; FIXME:
|
|
;; `ispell-valid-dictionary-list' should be calling backend-specific code,
|
|
;; and not check all possible backends by itself.
|
|
(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 behavior."
|
|
(skip-unless (executable-find "hunspell"))
|
|
(skip-unless (equal 0 (call-process "hunspell" nil nil nil)))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name "hunspell")
|
|
(ispell-library-directory temporary-file-directory))
|
|
(ispell-check-version t)
|
|
(should
|
|
(equal
|
|
(ispell-valid-dictionary-list)
|
|
'("default"))))
|
|
)
|
|
|
|
;; FIXME:
|
|
;; `ispell-valid-dictionary-list' should be calling backend-specific code,
|
|
;; and not check all possible backends by itself.
|
|
(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 behavior."
|
|
(skip-unless (executable-find "enchant-2"))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name "enchant-2")
|
|
(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<))))
|
|
)
|
|
|
|
;; FIXME:
|
|
;; `ispell-valid-dictionary-list' should be calling backend-specific code,
|
|
;; and not check all possible backends by itself.
|
|
(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 behavior."
|
|
(skip-unless (executable-find "enchant-2"))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name "enchant-2")
|
|
(ispell-library-directory temporary-file-directory))
|
|
(ispell-check-version t)
|
|
(should
|
|
(equal
|
|
(ispell-valid-dictionary-list)
|
|
'("default"))))
|
|
)
|
|
|
|
(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))))
|
|
(ispell-tests--letopt
|
|
((ispell-library-directory ispell-library-directory)
|
|
(ispell-local-dictionary-alist ispell-local-dictionary-alist)
|
|
(ispell-program-name "ispell"))
|
|
(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)))
|
|
(setopt ispell-local-dictionary-alist
|
|
'(("english" "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") nil utf-8)))
|
|
(should (member "english" (ispell-valid-dictionary-list)))
|
|
(should (member "default" (ispell-valid-dictionary-list))))
|
|
)
|
|
|
|
(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)))
|
|
(skip-unless
|
|
(with-temp-buffer
|
|
(call-process "aspell" nil t nil "dicts")
|
|
(goto-char 1)
|
|
(let ((ht (make-hash-table :test 'equal))
|
|
(has-duplicates-p nil))
|
|
(while
|
|
(progn
|
|
(let* ((l (buffer-substring-no-properties
|
|
(line-beginning-position)
|
|
(line-end-position)))
|
|
(_ (setf has-duplicates-p (hash-table-contains-p l ht)))
|
|
(_ (puthash l t ht))
|
|
(endfile (forward-line)))
|
|
(and (not has-duplicates-p) (= 0 endfile))))
|
|
t)
|
|
(when has-duplicates-p
|
|
(message "Your aspell setup is unconventional: %s Cannot run tests."
|
|
"it has duplicate dictionaries. "))
|
|
(not has-duplicates-p))))
|
|
(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
|
|
(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 "
|
|
<!--
|
|
" ispell-words-keyword " " testword "
|
|
-->
|
|
"))))))
|
|
|
|
(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-words-keyword "
|
|
-->
|
|
")
|
|
(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/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."
|
|
(ispell-tests--letopt ((ispell-program-name "ispell"))
|
|
(with-temp-buffer
|
|
(let* ((testword (format "%s" (random)))
|
|
(fill-column 50))
|
|
(nxml-mode)
|
|
(insert "
|
|
<!-- " ispell-words-keyword (make-string fill-column ?a) "
|
|
-->
|
|
")
|
|
(ispell-add-per-file-word-list testword)
|
|
(should (equal (buffer-string)
|
|
(concat "
|
|
<!-- " ispell-words-keyword (make-string fill-column ?a)
|
|
"
|
|
" ispell-words-keyword " " testword "
|
|
-->
|
|
")))))))
|
|
|
|
;; 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 footer to backend.
|
|
Should pass regardless of the backend and the dictionary, because
|
|
presumably nobody will have (`hellooooooo')
|
|
`ispell-tests--constants/english/wrong' in their dictionary."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(ispell-tests--letopt ((ispell-program-name (ispell-tests--some-backend)))
|
|
(ispell-tests--with-ispell-global-dictionary
|
|
nil
|
|
(with-temp-buffer
|
|
(nxml-mode)
|
|
(ignore-errors (ispell-kill-ispell))
|
|
(ispell-init-process)
|
|
(let ((test-output (ispell--run-on-word ispell-tests--constants/english/wrong)))
|
|
(should (listp test-output))
|
|
(should-not (equal t test-output)))
|
|
(ispell-add-per-file-word-list ispell-tests--constants/english/wrong)
|
|
(ispell-buffer-local-words)
|
|
(should (equal t (ispell--run-on-word ispell-tests--constants/english/wrong)))))))
|
|
)
|
|
|
|
|
|
(ert-deftest
|
|
ispell/ispell-buffer-local-words/ispell-buffer-session-localwords ()
|
|
"Send some words prefixed by @ from the file's footer to backend.
|
|
Should pass regardless of the backend and the dictionary, because
|
|
presumably nobody will have `ispell-tests--constants/english/wrong'
|
|
\(`hellooooooo') in their dictionary."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(ispell-tests--letopt ((ispell-program-name (ispell-tests--some-backend))
|
|
(ispell-dictionary nil))
|
|
(ispell-tests--with-ispell-global-dictionary
|
|
nil
|
|
(cd temporary-file-directory)
|
|
(with-temp-buffer
|
|
(nxml-mode)
|
|
(ignore-errors (ispell-kill-ispell))
|
|
(ispell-init-process)
|
|
(let ((test-output (ispell--run-on-word ispell-tests--constants/english/wrong)))
|
|
(should (listp test-output))
|
|
(should-not (equal t test-output)))
|
|
(let ((ispell-buffer-session-localwords (list ispell-tests--constants/english/wrong)))
|
|
(ispell-buffer-local-words)
|
|
(should (equal t (ispell--run-on-word ispell-tests--constants/english/wrong))))))))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-init-process/works-no-home ()
|
|
"Simple test to check that `ispell-init-process' works."
|
|
:expected-result :failed
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(ispell-tests--with-ispell-global-dictionary nil
|
|
(ispell-tests--letopt ((ispell-program-name (ispell-tests--some-backend)))
|
|
(with-temp-buffer
|
|
(with-environment-variables
|
|
(("HOME" (make-temp-name temporary-file-directory)))
|
|
(ispell-init-process))))
|
|
'passed)
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-init-process/works-with-home ()
|
|
"Simple test to check that `ispell-init-process' works."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(ispell-tests--letopt ((ispell-program-name (ispell-tests--some-backend)))
|
|
(ispell-tests--with-ispell-global-dictionary
|
|
nil
|
|
(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/lispellnonexistent.txt"
|
|
"Q:\\ispellnonexistent\\ispellnonexistent.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-overridden ()
|
|
"ispell.el can recognize keyword-defined dictionary and keyword-defined
|
|
personal-dictionary."
|
|
(with-temp-buffer
|
|
(nxml-mode)
|
|
(let ((test-dict "ispellnonexistent"))
|
|
(seq-map (lambda (test-pdict)
|
|
(insert
|
|
(car ispell-tests--constants/english/correct-list) "\n\n\n"
|
|
"<!-- " ispell-dictionary-keyword test-dict " -->"
|
|
"<!-- " ispell-pdict-keyword test-pdict " -->")
|
|
(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-overridden ()
|
|
"ispell.el can recognize keyword-defined dictionary and keyword-defined
|
|
personal-dictionary."
|
|
:expected-result :failed
|
|
(with-temp-buffer
|
|
(nxml-mode)
|
|
(let ((test-dict "ispellnonexistent"))
|
|
(seq-map (lambda (test-pdict)
|
|
(insert
|
|
(car ispell-tests--constants/english/correct-list) "\n\n\n"
|
|
"<!-- " ispell-dictionary-keyword test-dict " -->"
|
|
"<!-- " ispell-pdict-keyword test-pdict " -->")
|
|
(ispell-tests--letopt ((ispell-current-dictionary "ispellnonexistent2"))
|
|
(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+overridden ()
|
|
"ispell.el can recognize 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 "ispellnonexistent3"))
|
|
(seq-map (lambda (test-pdict)
|
|
(insert
|
|
(car ispell-tests--constants/english/correct-list) "\n\n\n"
|
|
"<!-- " ispell-dictionary-keyword test-dict " -->"
|
|
"<!-- " ispell-pdict-keyword test-pdict " -->")
|
|
(ispell-tests--letopt ((ispell-current-dictionary "ispellnonexistent4"))
|
|
(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+overridden ()
|
|
"ispell.el can recognize 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 "ispellnonexistent5"))
|
|
(seq-map
|
|
(lambda (test-pdict)
|
|
(insert
|
|
(car ispell-tests--constants/english/correct-list) "\n\n\n"
|
|
"<!-- " ispell-dictionary-keyword test-dict " -->"
|
|
"<!-- " ispell-pdict-keyword test-pdict " -->")
|
|
(ispell-tests--letopt ((ispell-current-dictionary "ispellnonexistent4"))
|
|
(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 successfully 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"))
|
|
(ispell-tests--letopt
|
|
((ispell-parser 'ispellnonexistent)
|
|
(ispell-local-dictionary-alist
|
|
`((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']"
|
|
nil ("-B") ,test-extcharmode utf-8)))
|
|
(ispell-current-dictionary test-dictname))
|
|
|
|
(insert
|
|
(car ispell-tests--constants/english/correct-list)"\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)
|
|
(let ((ispell-really-hunspell nil))
|
|
(ispell-buffer-local-parsing)))
|
|
(advice-remove 'ispell-send-string #'checker)))))))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-buffer-local-parsing/local-keyword/hunspell-bug ()
|
|
"Check that ispell.el can successfully pick up a tex parser from a buffer-local keyword."
|
|
;; FIXME: what if default dictionary sets
|
|
;; (ispell-get-extended-character-mode) ?
|
|
:expected-result :failed
|
|
(with-temp-buffer
|
|
(let ((test-parser "~tex")
|
|
(test-dictname "testdict")
|
|
(test-extcharmode "~latin3"))
|
|
(ispell-tests--letopt ((ispell-parser 'ispellnonexistent)
|
|
(ispell-local-dictionary-alist
|
|
`((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']"
|
|
nil ("-B") ,test-extcharmode utf-8)))
|
|
(ispell-current-dictionary test-dictname))
|
|
|
|
(insert
|
|
ispell-tests--constants/english/correct-one "\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)
|
|
(let ((ispell-really-hunspell t))
|
|
(ispell-buffer-local-parsing)))
|
|
(advice-remove 'ispell-send-string #'checker)))))))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-buffer-local-parsing/mode-tex ()
|
|
"Check that ispell.el can successfully 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))
|
|
(ispell-tests--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
|
|
(car ispell-tests--constants/english/correct-list) "\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 successfully pick up an extended character mode from the dictionary."
|
|
(with-temp-buffer
|
|
(insert
|
|
ispell-tests--constants/english/correct-one "\n\n\n")
|
|
(let ((test-extcharmode "~latin3"))
|
|
(ispell-tests--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.
|
|
This does not work well on hunspell, because hunspell
|
|
lies in their Man page, and in enchant-2 when it is using
|
|
hunspell. Hence skipping."
|
|
(skip-unless (not (or (equal (ispell-tests--some-backend)
|
|
"hunspell")
|
|
(equal (ispell-tests--some-backend)
|
|
"enchant-2"))))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(with-temp-buffer
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend)))
|
|
|
|
(ispell-check-version)
|
|
(if (and ispell-really-aspell
|
|
(equal ispell-program-name "ispell"))
|
|
;; Don't let Aspell hide its true nature.
|
|
(setq ispell-program-name "aspell"))
|
|
(let ((test-dictname (ispell-tests--some-valid-dictionary ispell-program-name))
|
|
(test-extcharmode "~latin3")
|
|
(test-parser "~testparser")
|
|
(test-localword1 "aaaaaaaaaaaaa")
|
|
(test-localword2 "bbbbbbbbbbb")
|
|
(test-pdict "test-pdict.pdict"))
|
|
(insert
|
|
(car ispell-tests--constants/english/correct-list) "\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")
|
|
(ispell-tests--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-accept-buffer-local-defs/received-file ()
|
|
"Check that `ispell-accept-buffer-local-defs' is broken when a file has a nonexistent file-local dictionary.
|
|
We do not control this data, but this should make ispell.el fail."
|
|
:expected-result :failed
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(with-temp-buffer
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend)))
|
|
|
|
(let ((test-dictname ispell-tests--constants/nonexistent-dictionary))
|
|
(insert
|
|
(car ispell-tests--constants/english/correct-list) "\n\n\n"
|
|
ispell-dictionary-keyword test-dictname "\n")
|
|
(ispell-tests--letopt
|
|
((ispell-check-comments t)
|
|
(ispell-parser 'tex))
|
|
|
|
(tex-mode)
|
|
(progn
|
|
(ignore-errors (ispell-kill-ispell))
|
|
;; should not error
|
|
(ispell-accept-buffer-local-defs)
|
|
)))))))
|
|
|
|
(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))
|
|
(ispell-tests--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 ispell-tests--constants/english/wrong)))
|
|
(should (listp test-output))
|
|
(should-not (equal t test-output))
|
|
(setq ispell-filter nil)
|
|
(setq ispell-filter-continue nil))
|
|
(mapc (lambda (word)
|
|
(let ((test-output (ispell--run-on-word word)))
|
|
(should-not (listp test-output))
|
|
(should (equal t test-output))
|
|
(setq ispell-filter nil)
|
|
(setq ispell-filter-continue nil)))
|
|
ispell-tests--constants/english/correct-list)
|
|
))))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell--run-on-word/default/fails ()
|
|
"Check `ispell--run-on-word', 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")))
|
|
(ispell-tests--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 ispell-tests--constants/english/wrong)))
|
|
(should (listp test-output))
|
|
(should-not (equal t test-output)))
|
|
|
|
(let ((test-output (ispell--run-on-word (car ispell-tests--constants/english/correct-list))))
|
|
(should-not (listp test-output))
|
|
(should (equal t test-output)))
|
|
|
|
))))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-word/default/check-only/correct ()
|
|
"Check that `ispell-word' works with a default dictionary.
|
|
We expect it to be english, as Ispell ships it."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(skip-unless (equal
|
|
0
|
|
(call-process (ispell-tests--some-backend) nil nil nil "-vv")))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let ((default-directory temporary-file-directory))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend))
|
|
(ispell-dictionary nil))
|
|
|
|
(ignore-errors (ispell-kill-ispell t t))
|
|
(with-temp-buffer
|
|
(insert
|
|
(car ispell-tests--constants/english/correct-list) "\n")
|
|
(goto-char 1)
|
|
(ert-with-message-capture lres
|
|
(ispell-word)
|
|
(should (string-match "is correct" lres))))
|
|
'passed)))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-word/default/check-only/correct/add-init ()
|
|
"Check that `ispell-word' works with a default dictionary.
|
|
We expect it to be english, as Ispell ships it. This test is different
|
|
from the previous one in that an explicit init call
|
|
to (`ispell-init-process') is added. I had issues with it, so I would like
|
|
to test it explicitly."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(skip-unless (equal
|
|
0
|
|
(call-process (ispell-tests--some-backend) nil nil nil "-vv")))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let ((default-directory temporary-file-directory))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend))
|
|
(ispell-dictionary nil)
|
|
(ispell-check-only t))
|
|
|
|
(ignore-errors (ispell-kill-ispell t t))
|
|
(with-temp-buffer
|
|
(ispell-init-process) ;; this is added
|
|
(insert
|
|
(car ispell-tests--constants/english/correct-list) "\n")
|
|
(goto-char 1)
|
|
(ert-with-message-capture lres
|
|
(ispell-word)
|
|
(should (string-match "is correct" lres)))
|
|
'passed
|
|
))))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-word/default/check-only/incorrect ()
|
|
"Check that `ispell-word' works with a default dictionary.
|
|
We expect it to be english, as Ispell ships it. This test gives it a
|
|
word which does not exist."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let ((default-directory temporary-file-directory))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend))
|
|
(ispell-dictionary nil)
|
|
(ispell-check-only t))
|
|
|
|
(ignore-errors (ispell-kill-ispell t t))
|
|
(with-temp-buffer
|
|
(insert
|
|
ispell-tests--constants/english/wrong "\n")
|
|
(goto-char 1)
|
|
(ert-with-message-capture lres
|
|
(ispell-word)
|
|
(should (string-match "is incorrect" lres))
|
|
'passed)))))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-region/correct ()
|
|
"The simplest test for `ispell-region'."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory)
|
|
(words ispell-tests--constants/english/correct-list)
|
|
(text (string-join words " ")))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name
|
|
(ispell-tests--some-backend))
|
|
(ispell-dictionary nil))
|
|
|
|
(ignore-errors (ispell-kill-ispell t t))
|
|
(with-temp-buffer
|
|
(insert text)
|
|
(goto-char (length (nth 0 words)))
|
|
(ert-with-message-capture lres
|
|
(ispell-region (point) (point-max))
|
|
(should (string-match "^Spell-checking region using .* with .* dictionary...done" lres))
|
|
'passed)))))
|
|
)
|
|
|
|
|
|
|
|
(ert-deftest ispell/ispell-region/incorrect ()
|
|
"The simplest test for `ispell-region'."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory)
|
|
(words (list
|
|
(nth 0 ispell-tests--constants/english/correct-list)
|
|
ispell-tests--constants/english/wrong
|
|
(nth 1 ispell-tests--constants/english/correct-list)))
|
|
(text (string-join words " ")))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend))
|
|
(ispell-dictionary nil)
|
|
(ispell-check-only t))
|
|
|
|
(ignore-errors (ispell-kill-ispell t t))
|
|
(with-temp-buffer
|
|
(insert text)
|
|
(goto-char (length (nth 0 words)))
|
|
(cl-labels ((checker ()
|
|
(user-error "Expected error")))
|
|
(unwind-protect
|
|
(progn
|
|
(advice-add 'ispell-show-choices :override #'checker)
|
|
(should-error (ispell-region (point) (point-max))))
|
|
(advice-remove 'ispell-show-choices #'checker))))
|
|
'passed)))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-buffer/correct ()
|
|
"The simplest test for `ispell-buffer'.
|
|
`ispell-buffer' is a very simple wrapper around `ispell-region',
|
|
so this test virtually mirrors the previous one."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory)
|
|
(words ispell-tests--constants/english/correct-list)
|
|
(text (string-join words " ")))
|
|
(ispell-tests--letopt
|
|
((ispell-dictionary nil)
|
|
(ispell-program-name (ispell-tests--some-backend)))
|
|
|
|
(ignore-errors (ispell-kill-ispell t t))
|
|
(with-temp-buffer
|
|
(insert text)
|
|
(ert-with-message-capture lres
|
|
(ispell-buffer)
|
|
(should (string-match "^Spell-checking .* using .* with .* dictionary...done" lres))
|
|
)))))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-buffer/incorrect ()
|
|
"The simplest test for `ispell-buffer'.
|
|
`ispell-buffer' is a very simple wrapper around `ispell-region',
|
|
so this test virtually mirrors the previous one."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory)
|
|
(words (append ispell-tests--constants/english/correct-list
|
|
(list ispell-tests--constants/english/wrong)))
|
|
(text (string-join words " ")))
|
|
(ispell-tests--letopt
|
|
((ispell-dictionary nil)
|
|
(ispell-program-name (ispell-tests--some-backend)))
|
|
|
|
(ignore-errors (ispell-kill-ispell t t))
|
|
(with-temp-buffer
|
|
(insert text)
|
|
;; This is intentional. The incorrect word is not in the region,
|
|
;; but `ispell-buffer' should move the point to the beginning
|
|
;; of the buffer.
|
|
(goto-char (length (nth 0 words)))
|
|
(cl-labels
|
|
((checker ()
|
|
(user-error "Expected error")))
|
|
(unwind-protect
|
|
(progn
|
|
(advice-add 'ispell-show-choices :override
|
|
#'checker)
|
|
(should-error (ispell-buffer))
|
|
(ispell-kill-ispell nil t)
|
|
'passed)
|
|
(advice-remove 'ispell-show-choices #'checker)))
|
|
))))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-kill-ispell ()
|
|
"Test that killing ispell works."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory)
|
|
(words (append ispell-tests--constants/english/correct-list
|
|
(list ispell-tests--constants/english/wrong)))
|
|
(text (string-join words " ")))
|
|
(with-temp-buffer
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend)))
|
|
(ispell-tests--with-ispell-global-dictionary
|
|
nil
|
|
(insert text)
|
|
(ispell-init-process)
|
|
(should ispell-async-processp)
|
|
(should (eq (ispell-process-status) 'run))
|
|
(ispell-kill-ispell nil t)))
|
|
'passed
|
|
)))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell/buffer ()
|
|
"Test `ispell-buffer'.
|
|
`ispell' is just a wrapper around `ispell-region'
|
|
and `ispell-buffer', which is also a wrapper around
|
|
`ispell-buffer'.
|
|
This test might seem confusing, as it does not check
|
|
for the availability of the backend, but this does
|
|
not matter `ispell' function does not use the
|
|
backend."
|
|
(let ((transient-mark-mode t))
|
|
(with-temp-buffer
|
|
(insert (string-join ispell-tests--constants/english/correct-list " "))
|
|
(goto-char 2)
|
|
(set-mark (point))
|
|
(goto-char (point-max))
|
|
(deactivate-mark)
|
|
(cl-labels ((checker-buffer ()
|
|
t)
|
|
(checker-region (_a _b)
|
|
(user-error "Test failed")))
|
|
(unwind-protect
|
|
(progn
|
|
(advice-add 'ispell-buffer :override #'checker-buffer)
|
|
(advice-add 'ispell-region :override #'checker-region)
|
|
(ispell)
|
|
)
|
|
(progn
|
|
(advice-remove 'ispell-buffer #'checker-buffer)
|
|
(advice-remove 'ispell-region #'checker-region))))
|
|
))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell/region ()
|
|
"Test `ispell-region'.
|
|
`ispell' is just a wrapper around `ispell-region'
|
|
and `ispell-buffer', which is also a wrapper around
|
|
`ispell-buffer'."
|
|
(let ((transient-mark-mode t))
|
|
(with-temp-buffer
|
|
(insert (string-join ispell-tests--constants/english/correct-list " "))
|
|
(goto-char 2)
|
|
(set-mark (point))
|
|
(goto-char (point-max))
|
|
(activate-mark)
|
|
(cl-labels ((checker-buffer ()
|
|
(user-error "Test failed"))
|
|
(checker-region (_a _b)
|
|
t))
|
|
(unwind-protect
|
|
(progn
|
|
(advice-add 'ispell-buffer :override #'checker-buffer)
|
|
(advice-add 'ispell-region :override #'checker-region)
|
|
(ispell)
|
|
)
|
|
(progn
|
|
(advice-remove 'ispell-buffer #'checker-buffer)
|
|
(advice-remove 'ispell-region #'checker-region))))
|
|
))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-change-dictionary ()
|
|
"Simple test for changing a dictionary."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend)))
|
|
(with-temp-buffer
|
|
(ispell-change-dictionary "english")
|
|
(should (equal ispell-local-dictionary "english"))
|
|
(ispell-change-dictionary "default")
|
|
(should (equal ispell-local-dictionary nil))
|
|
(ispell-change-dictionary "english")
|
|
(should (equal ispell-local-dictionary "english"))
|
|
'passed
|
|
))))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-comments-and-strings/correct ()
|
|
"Test that `ispell-comments-and-strings' does not err on a correct buffer."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory))
|
|
(ispell-tests--letopt
|
|
((ispell-dictionary nil)
|
|
(ispell-program-name (ispell-tests--some-backend)))
|
|
(with-temp-buffer
|
|
(ispell-kill-ispell t t)
|
|
(insert "#!/bin/bash\n"
|
|
"echo \""
|
|
(string-join
|
|
ispell-tests--constants/english/correct-list " ")
|
|
"\"\n"
|
|
"# commented line\n")
|
|
(sh-mode)
|
|
(ert-with-message-capture lres
|
|
(ispell-comments-and-strings)
|
|
(should (string-match "Spell-checking .* using .* with .* dictionary...done" lres))))
|
|
'passed
|
|
)))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-comments-and-strings/incorrect ()
|
|
"Test that `ispell-comments-and-strings' errs on a correct buffer."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend)))
|
|
(with-temp-buffer
|
|
(insert "#!/bin/bash\n"
|
|
"echo \"" (string-join
|
|
ispell-tests--constants/english/correct-list " ")
|
|
"\"\n"
|
|
"# " ispell-tests--constants/english/wrong "\n")
|
|
(sh-mode)
|
|
(ert-with-message-capture lres
|
|
(should-error (ispell-comments-and-strings)))
|
|
'passed
|
|
))))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-comment-or-string-at-point ()
|
|
"Test that `ispell-comment-or-string-at-point' runs two tests.
|
|
One correct an one incorrect in the same buffer."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory))
|
|
(ispell-tests--letopt
|
|
((ispell-dictionary nil)
|
|
(ispell-program-name (ispell-tests--some-backend)))
|
|
(with-temp-buffer
|
|
(let ((string1 "#!/bin/bash\necho\"")
|
|
(string2 (string-join
|
|
ispell-tests--constants/english/correct-list " "))
|
|
(string3 "\"\n# ")
|
|
(string4 ispell-tests--constants/english/wrong)
|
|
(string5 "\n"))
|
|
(insert string1
|
|
string2
|
|
string3
|
|
string4
|
|
string5)
|
|
(sh-mode)
|
|
(goto-char (+ (length string1) (length (nth 0 ispell-tests--constants/english/correct-list))))
|
|
(ert-with-message-capture lres
|
|
(ispell-comment-or-string-at-point)
|
|
(should (string-match "Spell-checking .* using .* with .* dictionary...done" lres)))
|
|
(goto-char (+ (length string1)
|
|
(length string2)
|
|
(length string3)
|
|
1))
|
|
(should-error (ispell-comment-or-string-at-point)))
|
|
'passed))))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-pdict-save ()
|
|
"Simple `ispell-pdict-save' test."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend)))
|
|
(with-temp-buffer
|
|
(insert (format "%s" (random)))
|
|
(ispell-kill-ispell t t)
|
|
(ispell-pdict-save t t)
|
|
'passed))))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-pdict-save/force ()
|
|
"Simple `ispell-pdict-save' test."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend)))
|
|
(with-temp-buffer
|
|
(insert (format "%s" (random)))
|
|
(goto-char 1)
|
|
(ispell-kill-ispell t t)
|
|
(ispell-pdict-save t t)
|
|
'passed))))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-pdict-save/modified ()
|
|
"Simple `ispell-pdict-save' test."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend-available-p)))
|
|
(with-temp-buffer
|
|
(insert (format "%s" (random)))
|
|
(goto-char 1)
|
|
(ispell-kill-ispell t t)
|
|
(cl-labels
|
|
((checker (s)
|
|
(should (equal s "#\n"))))
|
|
(unwind-protect
|
|
(progn
|
|
(advice-add 'ispell-send-string :override
|
|
#'checker)
|
|
(let ((ispell-pdict-modified-p t))
|
|
(ispell-pdict-save t nil)))
|
|
(advice-remove 'ispell-send-string #'checker))))
|
|
'passed)))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-pdict-save/unmodified ()
|
|
"Simple `ispell-pdict-save' test."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend)))
|
|
(with-temp-buffer
|
|
(insert (format "%s" (random)))
|
|
(goto-char 1)
|
|
(ispell-kill-ispell t t)
|
|
(cl-labels ((checker (_s)
|
|
(user-error "Test failed")))
|
|
(unwind-protect
|
|
(progn
|
|
(advice-add 'ispell-send-string :override
|
|
#'checker)
|
|
(let ((ispell-pdict-modified-p nil))
|
|
(ispell-pdict-save t nil)))
|
|
(advice-remove 'ispell-send-string #'checker))))
|
|
'passed)))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-lookup-words/simple ()
|
|
"Test if `ispell-lookup-words' is runnable."
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory)
|
|
(tempfile (make-temp-file "emacs-ispell.el-test" nil nil ispell-tests--constants/completion)))
|
|
(ispell-tests--letopt
|
|
((ispell-complete-word-dict tempfile))
|
|
(with-temp-buffer
|
|
(insert (substring ispell-tests--constants/completion 0 -2))
|
|
(unwind-protect
|
|
(progn
|
|
(should (equal
|
|
(ispell-lookup-words (substring ispell-tests--constants/completion 0 -2))
|
|
(list ispell-tests--constants/completion)))
|
|
(should (equal
|
|
(ispell-lookup-words ispell-tests--constants/english/wrong)
|
|
nil)))
|
|
(delete-file tempfile)))
|
|
'passed)))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-complete-word/ispell-completion-at-point ()
|
|
"Test if `ispell-complete-word' and `ispell-completion-at-point' are runnable."
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory)
|
|
(tempfile (make-temp-file "emacs-ispell.el-test" nil nil ispell-tests--constants/completion)))
|
|
(ignore-errors (ispell-kill-ispell t t))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend))
|
|
(ispell-complete-word-dict tempfile))
|
|
(ispell-tests--with-ispell-global-dictionary
|
|
nil
|
|
(with-temp-buffer
|
|
(insert (substring ispell-tests--constants/completion 0 -2))
|
|
(cl-labels ((my-ispell-command-loop (_p _n _w _s _e)
|
|
(car (nth 2 (ispell-completion-at-point)))))
|
|
(unwind-protect
|
|
(progn
|
|
(advice-add 'ispell-command-loop :override
|
|
#'my-ispell-command-loop)
|
|
(should (equal (car (nth 2 (ispell-completion-at-point)))
|
|
ispell-tests--constants/completion))
|
|
(ispell-complete-word)
|
|
(should (equal ispell-tests--constants/completion (buffer-string))))
|
|
(progn
|
|
(delete-file tempfile)
|
|
(advice-remove
|
|
'ispell-command-loop
|
|
#'my-ispell-command-loop)))))
|
|
'passed))))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-complete-word-interior-frag/simple ()
|
|
"Test if `ispell-complete-word-interior-frag' is runnable."
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory)
|
|
(tempfile (make-temp-file "emacs-ispell.el-test" nil nil "waveguides")))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend))
|
|
(ispell-complete-word-dict tempfile))
|
|
(ispell-tests--with-ispell-global-dictionary
|
|
nil
|
|
(with-temp-buffer
|
|
(insert (substring ispell-tests--constants/completion 0 -2))
|
|
(cl-labels
|
|
((my-ispell-command-loop (_p _n _w _s _e)
|
|
(car (nth 2 (ispell-completion-at-point)))))
|
|
(unwind-protect
|
|
(progn
|
|
(advice-add 'ispell-command-loop :override
|
|
#'my-ispell-command-loop)
|
|
(goto-char 4)
|
|
(ispell-complete-word-interior-frag)
|
|
(should (equal ispell-tests--constants/completion
|
|
(buffer-string))))
|
|
(progn
|
|
(delete-file tempfile)
|
|
(advice-remove
|
|
'ispell-command-loop
|
|
#'my-ispell-command-loop)))))
|
|
'passed))))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-minor-mode/simple ()
|
|
"Try enabling `ispell-minor-mode' and test one test file."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend))
|
|
(ispell-dictionary nil))
|
|
(with-temp-buffer
|
|
(text-mode)
|
|
(ispell-minor-mode)
|
|
(insert ispell-tests--constants/english/wrong)
|
|
(set--this-command-keys " ")
|
|
(ert-with-message-capture lres
|
|
(ispell-minor-check)
|
|
(should (string-match
|
|
(seq-concatenate
|
|
'string
|
|
(upcase ispell-tests--constants/english/wrong)
|
|
" is incorrect")
|
|
lres)))
|
|
)
|
|
'passed)))
|
|
)
|
|
|
|
(ert-deftest ispell/ispell-message/correct ()
|
|
"Test that `ispell-message' works.
|
|
`ispell-message' is intended to be run before
|
|
a message is sent in `message-mode' or `mml-mode'."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend))
|
|
(ispell-dictionary nil))
|
|
(with-temp-buffer
|
|
(insert
|
|
(seq-concatenate
|
|
'string
|
|
"To:
|
|
Subject:
|
|
From: Anon <anon@anon>
|
|
Fcc: /tmp/234234.cb022f1a625b65b2.mainframe:2,S
|
|
User-Agent: mu4e 1.12.9; emacs 31.0.50
|
|
Date: Tue, 09 Sep 2025 07:43:58 +0800
|
|
Message-ID: <878qiov7b5.fsf@mainframe>
|
|
--text follows this line--
|
|
" (string-join
|
|
ispell-tests--constants/english/correct-list " ")
|
|
"
|
|
--
|
|
signature
|
|
"))
|
|
(message-mode)
|
|
(ert-with-message-capture lres
|
|
(ispell-message)
|
|
(should (not (string-match "is incorrect" lres)))
|
|
)
|
|
(set-buffer-modified-p nil)
|
|
)
|
|
'passed)))
|
|
)
|
|
(ert-deftest ispell/ispell-message/incorrect ()
|
|
"Test that `ispell-message' works.
|
|
`ispell-message' is intended to be run before
|
|
a message is sent in `message-mode' or `mml-mode'."
|
|
(skip-unless (ispell-tests--some-backend-available-p))
|
|
(with-environment-variables (("HOME" temporary-file-directory))
|
|
(let* ((default-directory temporary-file-directory))
|
|
(ispell-tests--letopt
|
|
((ispell-program-name (ispell-tests--some-backend)))
|
|
(with-temp-buffer
|
|
(insert
|
|
(seq-concatenate
|
|
'string
|
|
"To:
|
|
Subject:
|
|
From: Anon <anon@anon>
|
|
Fcc: /tmp/234234.cb022f1a625b65b2.mainframe:2,S
|
|
User-Agent: mu4e 1.12.9; emacs 31.0.50
|
|
Date: Tue, 09 Sep 2025 07:43:58 +0800
|
|
Message-ID: <878qiov7b5.fsf@mainframe>
|
|
--text follows this line--
|
|
<#part sign=pgpmime>
|
|
" ispell-tests--constants/english/wrong "
|
|
--
|
|
signature
|
|
"))
|
|
(message-mode)
|
|
(cl-labels ((checker ()
|
|
(user-error "Expected error")))
|
|
(unwind-protect
|
|
(progn
|
|
(advice-add 'ispell-show-choices :override #'checker)
|
|
(should-error (ispell-message)))
|
|
(progn
|
|
(advice-remove 'ispell-show-choices #'checker)
|
|
(set-buffer-modified-p nil)))))
|
|
'passed)))
|
|
)
|
|
|
|
|
|
(provide 'ispell-tests)
|
|
|
|
;;; ispell-tests.el ends here
|