From 4846ec48714171eabc51929feebe03b9603713b7 Mon Sep 17 00:00:00 2001 From: Lockywolf Date: Fri, 12 Sep 2025 16:02:48 +0200 Subject: [PATCH] Add tests to ispell.el interactive functions * lisp/textmodes/ispell.el (ispell-accept-output): Fix variable init. * test/lisp/textmodes/ispell-resources/fake-aspell-new.bash: Update mock aspell to be able to serve all tests. * test/lisp/textmodes/ispell-tests/ispell-aspell-tests.el: (ispell/aspell/ispell-word/english/correct): Implement. (ispell/aspell/ispell-word/english/incorrect): Implement. (ispell/aspell/ispell-word/english/wrong-language): Implement. * test/lisp/textmodes/ispell-tests/ispell-hunspell-tests.el Fix byte compilation errors. * test/lisp/textmodes/ispell-international-ispell-tests.el Fix byte compilation errors. * test/lisp/textmodes/ispell-tests/ispell-tests-common.el (with-ispell-global-dictionary): Implement a macro to set and restore ispell.el's global dictionary. * test/lisp/textmodes/ispell-tests/ispell-tests.el: (ispell/ispell-buffer-local-words/ispell-words-keyword): Fix CI run. (ispell/ispell-accept-buffer-local-defs/simple): Fix skip condition. (ispell/ispell--run-on-word/default): Fix skip condition. (ispell/ispell-word/default/check-only/correct): Fix global variable. (ispell/ispell-word/default/check-only/correct/add-init): Fix global variable. (ispell/ispell-word/default/check-only/incorrect): Fix skip condition. (ispell/ispell-region/incorrect): Fix postcondition. (ispell/ispell-call-process/simple): Fix emacs path. (ispell/ispell-call-process/simple-writable): Fix emacs path. (ispell/ispell-call-process-region/cat-empty): Fix emacs path. (ispell/ispell-call-process-region/cat-random): Fix emacs path. (ispell/ispell-kill-ispell): Implement. (ispell/ispell/buffer): Implement. (ispell/ispell/region): Implement. (ispell/ispell-change-dictionary): Implement. (ispell/ispell-comments-and-strings/correct): Implement. (ispell/ispell-comments-and-strings/incorrect): Implement. (ispell/ispell-comment-or-string-at-point): Implement. (ispell/ispell-pdict-save): Implement. (ispell/ispell-pdict-save/force): Implement. (ispell/ispell-pdict-save/modified): Implement. (ispell/ispell-pdict-save/unmodified): Implement. (ispell/ispell-lookup-words/simple): Implement. (ispell/ispell-complete-word/ispell-completion-at-point): Implement. (ispell/ispell-complete-word-interior-frag/simple): Implement. (ispell/ispell-minor-mode/simple): Implement. (ispell/ispell-message/correct): Implement. (ispell/ispell-message/incorrect): Implement. --- lisp/textmodes/ispell.el | 3 +- .../ispell-resources/fake-aspell-new.bash | 71 +- .../ispell-tests/ispell-aspell-tests.el | 110 ++- .../ispell-tests/ispell-hunspell-tests.el | 9 +- .../ispell-international-ispell-tests.el | 7 +- .../ispell-tests/ispell-tests-common.el | 54 +- .../textmodes/ispell-tests/ispell-tests.el | 678 +++++++++++++++--- 7 files changed, 800 insertions(+), 132 deletions(-) diff --git a/lisp/textmodes/ispell.el b/lisp/textmodes/ispell.el index ad7c8571f67..b37d07f5e62 100644 --- a/lisp/textmodes/ispell.el +++ b/lisp/textmodes/ispell.el @@ -1803,7 +1803,8 @@ and pass it the output of the last Ispell invocation." (if (null ispell-process) (error "No Ispell process to read output from!") (let ((buf ispell-output-buffer) - ispell-output) + (ispell-output nil)) + (if (not (bufferp buf)) (setq ispell-filter nil) (with-current-buffer buf diff --git a/test/lisp/textmodes/ispell-resources/fake-aspell-new.bash b/test/lisp/textmodes/ispell-resources/fake-aspell-new.bash index 7dd935bac3c..1eb004f6ba6 100755 --- a/test/lisp/textmodes/ispell-resources/fake-aspell-new.bash +++ b/test/lisp/textmodes/ispell-resources/fake-aspell-new.bash @@ -1,5 +1,20 @@ #!/bin/bash +#exec aspell "$@" + +#rm -rf ~/lwf_mock-aspell.log + +#printf 'date="%s"\n' "$(date --iso=seconds)" > /tmp/lwf_mock-aspell.log + +#printf 'args="%s"\n' "$*" >> /tmp/lwf_mock-aspell.log || { printf "lwf:ERROR\n" ; exit 3 ; } + +# coproc aspell { aspell "$@" ; } + +if [[ "$HOME" == '' ]] ; then + echo "HOME is unset. Aspell usually fails in such a case\n" 1>2 + exit 3 +fi + vv= show_vv() @@ -7,13 +22,51 @@ show_vv() printf '%s\n' "@(#) International Ispell Version 3.1.20 (but really Aspell 0.60.0)" } -imitate_repl() +imitate_pipe() { - while true ; do - read a -# printf 'debug="%s"\n' "$a" + local a + declare -A sessiondict + show_vv + while read a ; do + #printf 'pipe="%s"\n' "$a" >> /tmp/lwf_mock-aspell.log if [[ "$a" == '' ]] ; then printf '' + elif [[ "$a" == '+' || "$a" == '~nroff' || "$a" == '~tex' || "$a" == '!' || "$a" == '-' || "$a" == '%' ]] ; then + printf '' + elif [[ "${a:0:1}" == '@' ]] ; then + sessiondict["${a:1}"]="true" + printf '' + else + for b in $a ; do + if [[ "$b" == '^' ]] ; then + printf '' + elif [[ ${sessiondict[$b]} == 'true' || ${sessiondict[${b#^}]} == 'true' ]] ; then + printf '*\n' + elif [[ "$b" == '^tarampampamtararam' || "$b" == 'tarampampamtararam' ]] ; then + printf '# tarampampamtararam 0\n' # wrong word + elif [[ "$b" == '^badworddd' || "$b" == 'badworddd' ]] ; then + printf '# badworddd 0\n' # wrong word + elif [[ "$b" == '^hellooooooo' || "$b" == 'hellooooooo' ]] ; then + printf '# hellooooooo 0\n' # wrong word + elif [[ "$b" == '^' ]] ; then + printf '\n' + else + printf "*\n" + fi + done + printf '\n' + fi + done +} + +imitate_interactive() +{ + exit 6 + while true ; do + read a +# printf 'interactive="%s"\n' "$a" >> /tmp/lwf_mock-aspell.log + if [[ "$a" == '' ]] ; then + printf '\n' elif [[ "$a" == 'tarampampamtararam' ]] ; then printf '# tarampampamtararam 0\n\n' # wrong word else @@ -22,16 +75,16 @@ imitate_repl() done } -show_vv while :; do case $1 in -vv|-v) - #show_vv # for ispell.el error detection + show_vv # for ispell.el version detection exit ;; -a) # imitate REPL - imitate_repl + imitate_pipe + exit ;; -?*) printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2 @@ -41,3 +94,7 @@ while :; do esac shift done + +printf 'Usage: aspell [options] \n' + +#printf 'this place should be unreachable\n' >> /tmp/lwf_mock-aspell.log diff --git a/test/lisp/textmodes/ispell-tests/ispell-aspell-tests.el b/test/lisp/textmodes/ispell-tests/ispell-aspell-tests.el index c24d284f426..66451bf4548 100644 --- a/test/lisp/textmodes/ispell-tests/ispell-aspell-tests.el +++ b/test/lisp/textmodes/ispell-tests/ispell-aspell-tests.el @@ -25,8 +25,16 @@ ;;; Code: (require 'ispell) +(require 'ert) +(require 'ert-x) -(load (expand-file-name "test/lisp/textmodes/ispell-tests/ispell-tests-common.el" source-directory)) +(eval-and-compile + (add-to-list 'load-path (when (not (null load-file-name)) + (directory-file-name + (file-name-directory load-file-name)))) + (load "ispell-tests-common.el")) + +(declare-function letopt (expand-file-name "test/lisp/textmodes/ispell-tests/ispell-tests-common.el" source-directory) t t) (ert-deftest ispell/aspell/ispell-check-version/works () "Test that aspell is correctly detected." @@ -68,6 +76,106 @@ (set-variable 'ispell-last-program-name test-saved-ispell-last-program-name))))) +(ert-deftest ispell/aspell/ispell-word/english/correct () +"This test checks that Russian spellchecking works for Aspell." + (skip-unless (executable-find "aspell")) + (skip-unless (equal + 0 + (call-process "aspell" nil nil nil "-vv"))) + (skip-unless (equal + 0 + (with-temp-buffer + (insert "test") + (call-process-region + nil + nil + "aspell" nil t nil "-a" "-denglish")))) + (with-environment-variables (("HOME" temporary-file-directory)) + (let ((default-directory temporary-file-directory)) + (letopt ((ispell-program-name "aspell") + (ispell-dictionary "english")) + (ignore-errors (ispell-kill-ispell t t)) + (with-temp-buffer + (insert + "hello\n") + (goto-char 0) + (ispell-change-dictionary "english") + (let ((debugmessage "")) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word) + (setf debugmessage lres) + ;;(should (string-match "is correct" lres)) + )) + (message "lwf:lres=%s" debugmessage))) + 'passed + ))) + ) + +(ert-deftest ispell/aspell/ispell-word/english/incorrect () +"This test checks that Russian spellchecking works for Aspell." + (skip-unless (executable-find "aspell")) + (skip-unless (equal + 0 + (call-process "aspell" nil nil nil "-vv"))) + (skip-unless (equal + 0 + (with-temp-buffer + (insert "test") + (call-process-region + nil + nil + "aspell" nil t nil "-a" "-denglish")))) + (with-environment-variables (("HOME" temporary-file-directory)) + (let ((default-directory temporary-file-directory)) + (letopt ((ispell-program-name "aspell") + (ispell-dictionary "english")) + (ignore-errors (ispell-kill-ispell t t)) + (with-temp-buffer + (insert + ;; there is no such a word in English, I swear. + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + ) + (goto-char 0) + (ispell-change-dictionary "english") + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is incorrect" lres)))) + 'passed + ))) + ) + +(ert-deftest ispell/aspell/ispell-word/english/wrong-language () + "This test checks that Russian spellchecking works for Aspell." + :expected-result :failed + (skip-unless (executable-find "aspell")) + (skip-unless (equal + 0 + (call-process "aspell" nil nil nil "-vv"))) + (skip-unless (equal + 0 + (with-temp-buffer + (insert "test") + (call-process-region nil nil "aspell" nil '("*scratch*" t) nil "-a" "-denglish")))) + (with-environment-variables (("HOME" temporary-file-directory)) + (let ((default-directory temporary-file-directory)) + (letopt ((ispell-program-name "aspell")) + (ignore-errors (ispell-kill-ispell t t)) + (with-temp-buffer + (insert + ;; giving Aspell a wrong language should not fail + "привет\n" + ) + (goto-char 0) + (ispell-change-dictionary "english") + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (not (string-match "Error" lres))))) + 'passed + ))) + ) (provide 'tests-ispell-aspell) ;;; tests-ispell-aspell.el ends here diff --git a/test/lisp/textmodes/ispell-tests/ispell-hunspell-tests.el b/test/lisp/textmodes/ispell-tests/ispell-hunspell-tests.el index ae3d6e303b3..8b625276e28 100644 --- a/test/lisp/textmodes/ispell-tests/ispell-hunspell-tests.el +++ b/test/lisp/textmodes/ispell-tests/ispell-hunspell-tests.el @@ -25,8 +25,15 @@ ;;; Code: (require 'ispell) +(require 'ert) +(require 'ert-x) + +(eval-and-compile + (add-to-list 'load-path (when (not (null load-file-name)) + (directory-file-name + (file-name-directory load-file-name)))) + (load "ispell-tests-common.el")) -(load (expand-file-name "test/lisp/textmodes/ispell-tests/ispell-tests-common.el" source-directory)) (ert-deftest ispell/hunspell/ispell-word/english/check-only () "This test checks that Russian spellchecking works for Hunspell." (skip-unless (executable-find "hunspell")) diff --git a/test/lisp/textmodes/ispell-tests/ispell-international-ispell-tests.el b/test/lisp/textmodes/ispell-tests/ispell-international-ispell-tests.el index f232f26d10d..30df3a3340a 100644 --- a/test/lisp/textmodes/ispell-tests/ispell-international-ispell-tests.el +++ b/test/lisp/textmodes/ispell-tests/ispell-international-ispell-tests.el @@ -25,8 +25,11 @@ ;;; Code: (require 'ispell) - -(load (expand-file-name "test/lisp/textmodes/ispell-tests/ispell-tests-common.el" source-directory)) +(eval-and-compile + (add-to-list 'load-path (and load-file-name + (directory-file-name + (file-name-directory load-file-name)))) + (load "ispell-tests-common.el")) (ert-deftest ispell/international-ispell/ispell-word/russian/check-only () "This test checks that Russian spellchecking works for. diff --git a/test/lisp/textmodes/ispell-tests/ispell-tests-common.el b/test/lisp/textmodes/ispell-tests/ispell-tests-common.el index a6c4f1247ee..0692e11e567 100644 --- a/test/lisp/textmodes/ispell-tests/ispell-tests-common.el +++ b/test/lisp/textmodes/ispell-tests/ispell-tests-common.el @@ -1,21 +1,43 @@ ;;; common.el --- -*- lexical-binding: t; -*- -(defvar tests-ispell-data-directory - (expand-file-name "test/lisp/textmodes/ispell-resources/" source-directory)) +;; (defvar tests-ispell-data-directory +;; (expand-file-name "test/lisp/textmodes/ispell-resources/" source-directory)) +(require 'ert) +(require 'ert-x) -(let* ((backend-binaries (list "ispell" "aspell" "hunspell" "enchant-2")) - (filter-binaries (lambda () - (seq-filter - #'executable-find - backend-binaries)))) +(defvar tests-ispell-data-directory + (let ((ert-resource-directory-trim-right-regexp "-tests/.*-tests-common\\.el")) + (ert-resource-directory))) + +;;(message "lwf:tests-ispell-data-directory=%s" tests-ispell-data-directory) + +(defvar fake-aspell-path + (expand-file-name "fake-aspell-new.bash" tests-ispell-data-directory)) + + +(let* ((backend-binaries (list "ispell" "aspell" "hunspell" "enchant-2" fake-aspell-path) + ) + (filter-binaries (seq-filter + (lambda (b) + (and + (executable-find b) + ;; (equal 0 + ;; (with-temp-buffer + ;; (call-process b nil t "-v"))) + )) + backend-binaries))) (defun ispell-tests--some-backend-available-p () (not - (null (funcall filter-binaries)))) + (null filter-binaries))) (defun ispell-tests--some-backend () - (car (funcall filter-binaries)))) + (let ((retval (car filter-binaries))) + (message "available backend is:%s" retval) + retval))) +(eval-when-compile + (require 'cl-macs)) (cl-defmacro letopt (bindings &body body) (declare (indent 1)) (let* ((binding-var (lambda (binding) (car binding))) @@ -33,4 +55,18 @@ ,@body) ,@(reverse restorebindings))))) +(cl-defmacro with-ispell-global-dictionary (bindings &body body) + "This macro should not really be needed, but `ispell.el'. +Sets up dictionaries in a stupid way." + (declare (indent 1)) + (let* ((dictionary-val (car bindings)) + (temp-var (gensym 'old-dictionary))) + `(let ((,temp-var (symbol-value 'ispell-dictionary))) + (unwind-protect (progn (ispell-change-dictionary ,dictionary-val t) + ,@body) + (progn + (unwind-protect + (ispell-change-dictionary ,temp-var t) + (message "restoring original dictionary failed"))))))) + (provide 'ispell-tests-common) diff --git a/test/lisp/textmodes/ispell-tests/ispell-tests.el b/test/lisp/textmodes/ispell-tests/ispell-tests.el index 9e675443986..b711ee59793 100644 --- a/test/lisp/textmodes/ispell-tests/ispell-tests.el +++ b/test/lisp/textmodes/ispell-tests/ispell-tests.el @@ -24,9 +24,24 @@ ;;; Code: -(require 'ispell) -(load (expand-file-name "test/lisp/textmodes/ispell-tests/ispell-tests-common.el" source-directory)) +(require 'ispell) +(eval-and-compile + (add-to-list 'load-path (when (not (null load-file-name)) + (directory-file-name + (file-name-directory load-file-name)))) + (load "ispell-tests-common.el")) + +(declare-function letopt "ispell-tests-common") +(declare-function ispell-tests--some-backend "ispell-tests-common" t t) +(declare-function ispell-tests--some-backend-available-p "ispell-tests-common" t t) + + +(defconst ispell-tests-emacs-binary-path + (concat invocation-directory invocation-name)) + +(require 'ert) +(require 'ert-x) (defun warnings-buffer-exists-p () "Check if a buffer named \"*Warnings*\" exists." @@ -134,7 +149,7 @@ the backend's process exists." (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))")) + (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 () @@ -142,7 +157,9 @@ the backend's process exists." (should (with-temp-buffer (let ((default-directory temporary-file-directory)) - (ispell-call-process "emacs" nil t nil "--batch" "-Q" "--eval" "(message default-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 () @@ -161,7 +178,7 @@ makes it useless." (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))") + (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)))))) @@ -176,7 +193,7 @@ makes it useless." (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))") + (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 () @@ -450,18 +467,20 @@ nXML comments." 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"))))))) + (with-environment-variables (("HOME" temporary-file-directory)) + (with-ispell-global-dictionary nil + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (nxml-mode) + (ignore-errors (ispell-kill-ispell)) + (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 @@ -470,35 +489,44 @@ presumably nobody will have `hellooooooo' in their dictionary." 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")))))))) + (with-environment-variables (("HOME" temporary-file-directory)) + (with-ispell-global-dictionary nil + (letopt ((ispell-program-name (ispell-tests--some-backend)) + (ispell-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 "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 () +(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)) - (letopt ((ispell-program-name (ispell-tests--some-backend))) - (with-temp-buffer + (with-ispell-global-dictionary nil + (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-withhome () +(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)) - (letopt ((ispell-program-name (ispell-tests--some-backend))) - (with-temp-buffer - (with-environment-variables (("HOME" temporary-file-directory)) - (ispell-init-process))))) + (with-ispell-global-dictionary nil + (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' @@ -742,18 +770,17 @@ mode from the dictionary." batch mode. 1. local words 2. dictionary and pdict -3. parser and extcharmode" - (skip-unless (executable-find "ispell")) - (setq old-engine ispell-program-name) - (setopt ispell-program-name "ispell") - (ispell-check-version t) - (skip-unless (and (null ispell-really-aspell) - (null ispell-really-hunspell) - (null ispell-really-enchant))) - (setq ispell-program-name old-engine) +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 - (letopt ((ispell-program-name "ispell")) + (letopt ((ispell-program-name (ispell-tests--some-backend))) (let ((test-dictname "english") (test-extcharmode "~latin3") (test-parser "~testparser") @@ -806,9 +833,6 @@ batch mode. "`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)) @@ -890,21 +914,18 @@ be rewritten with a mock." (call-process (ispell-tests--some-backend) nil nil nil "-vv"))) (with-environment-variables (("HOME" temporary-file-directory)) (let ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name (ispell-tests--some-backend))) + (letopt ((ispell-program-name (ispell-tests--some-backend)) + (ispell-dictionary nil)) (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert "hello\n") (goto-char 0) - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) + (ert-with-message-capture lres (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should ( > (search-backward "is correct" nil t) - current-point))))))))) + (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 @@ -920,23 +941,21 @@ like to test it explicitly." (call-process (ispell-tests--some-backend) nil nil nil "-vv"))) (with-environment-variables (("HOME" temporary-file-directory)) (let ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name (ispell-tests--some-backend))) + (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 "hello\n") (goto-char 0) - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) + (ert-with-message-capture lres (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is correct" nil t) - current-point))) - )))))) + (should (string-match "is correct" lres))) + 'passed + )))) + ) (ert-deftest ispell/ispell-word/default/check-only/incorrect () "Check that `ispell-word' works with a default @@ -945,75 +964,110 @@ Ispell ships it. This is probably wrong and should be rewritten with a mock. This test gives it a word which does not exist." (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)) - (letopt ((ispell-program-name (ispell-tests--some-backend))) + (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 - "helloooo\n") + "hellooooooo\n") (goto-char 0) - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) + (ert-with-message-capture lres (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is incorrect" nil t) - current-point))) - )))))) + (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)) - (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) - (fake-aspell-path (expand-file-name - "./fake-aspell-new.bash" - tests-ispell-data-directory)) (words '("hello" "test" "test" "more" "obvious" "word")) (text (string-join words " "))) - (letopt ((ispell-program-name fake-aspell-path)) + (letopt ((ispell-program-name (ispell-tests--some-backend)) + (ispell-dictionary nil)) (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer - (insert - text) + (insert text) (goto-char (length (nth 0 words))) - (let (;(ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) + (ert-with-message-capture lres (ispell-region (point) (point-max)) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (re-search-backward "Spell-checking region using .* with .* dictionary...done" nil t) current-point)) - 'passed) - ))))) + (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)) - (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) - (fake-aspell-path "aspell") (words '("hello" "tarampampamtararam" "world")) (text (string-join words " "))) - (letopt ((ispell-program-name fake-aspell-path)) + (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) + (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 '("hello" "test" "test" "more" "obvious" "word")) + (text (string-join words " "))) + (letopt ((ispell-dictionary nil) + (ispell-program-name (ispell-tests--some-backend))) + (ignore-errors (ispell-kill-ispell t t)) + (with-temp-buffer + (insert text) + (goto-char (length (nth 0 words))) + (let ((current-point + (with-current-buffer "*Messages*" + (point)))) + (ispell-buffer) + (with-current-buffer "*Messages*" + (goto-char (point-max)) + (should (> (re-search-backward "^Spell-checking .* using .* with .* dictionary...done" nil t) current-point)) + 'passed) + ))))) + ) + +(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 '("tarampampamtararam" "test" "test" "more" "obvious" "word" "badworddd")) + (text (string-join words " "))) + (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"))) @@ -1021,11 +1075,413 @@ This test gives it a word which does not exist." (progn (advice-add 'ispell-show-choices :override #'checker) - (should-error (ispell-region (point) (point-max))) + (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 '("tarampampamtararam" "test" "test" "more" "obvious" "word")) + (text (string-join words " "))) + (with-temp-buffer + (with-ispell-global-dictionary nil + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (insert text) + (ispell-init-process) + (should ispell-async-processp) + (should (eq (ispell-process-status) 'run)) + (ispell-kill-ispell nil t))) + 'passed + ))) + (message "lwf:debug2:ispell-program-name=%s:ispell-dictionary=%s" + ispell-program-name ispell-dictionary) + ) + +(ert-deftest ispell/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 "hello world test test") + (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 () + "`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 "hello world test test") + (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)) + (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)) + (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 to check\"\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)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (insert "#!/bin/bash\n" + "echo \"string to check\"\n" + "# tarampampamtararam\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)) + (letopt ((ispell-dictionary nil) + (ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (insert "#!/bin/bash\n" + "echo \"string to check\"\n" + "# tarampampamtararam\n") + (sh-mode) + (goto-char 25) + (ert-with-message-capture lres + (ispell-comment-or-string-at-point) + (should (string-match "Spell-checking .* using .* with .* dictionary...done" lres))) + (goto-char 47) + (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)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (insert "test") + (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)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (insert "testttttt") + (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)) + (letopt ((ispell-program-name (ispell-tests--some-backend-available-p))) + (with-temp-buffer + (insert "testttttt") + (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)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (insert "testttttt") + (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 "waveguides"))) + (letopt ((ispell-complete-word-dict tempfile)) + (with-temp-buffer + (insert "waveguid") + (unwind-protect + (progn + (should (equal + (ispell-lookup-words "waveguid") + '("waveguides"))) + (should (equal + (ispell-lookup-words "sdfsdfasdfsadfasdfasdf") + 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 "waveguides"))) + (ignore-errors (ispell-kill-ispell t t)) + (with-ispell-global-dictionary nil + (letopt ((ispell-program-name (ispell-tests--some-backend)) + (ispell-complete-word-dict tempfile)) + (with-temp-buffer + (insert "waveguid") + (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))) + "waveguides")) + (ispell-complete-word) + (should (equal "waveguides" (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"))) + (with-ispell-global-dictionary nil + (letopt ((ispell-program-name (ispell-tests--some-backend)) + (ispell-complete-word-dict tempfile)) + (with-temp-buffer + (insert "waveguid") + (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 "waveguides" (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)) + (letopt ((ispell-program-name (ispell-tests--some-backend)) + (ispell-dictionary nil)) + (with-temp-buffer + (text-mode) + (ispell-minor-mode) + (insert "tarampampamtararam") + (set--this-command-keys " ") + (ert-with-message-capture lres + (ispell-minor-check) + (should (string-match "TARAMPAMPAMTARARAM 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)) + (letopt ((ispell-program-name (ispell-tests--some-backend)) + (ispell-dictionary nil)) + (with-temp-buffer + (insert + "To: +Subject: +From: 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-- +Hello World +-- +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)) + (letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (insert + "To: +Subject: +From: 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> +tarampampamtararam +-- +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))) + ) + + + +;;====================================================================== +;; On this "emacs page" I want to test that customise variables change +;; function behavior in the way are intended to do. + + + (provide 'tests-ispell) ;;; tests-ispell.el ends here