diff --git a/test/hypb-tests.el b/test/hypb-tests.el index 4cf70846..ff9b9935 100644 --- a/test/hypb-tests.el +++ b/test/hypb-tests.el @@ -3,7 +3,7 @@ ;; Author: Mats Lidell ;; ;; Orig-Date: 5-Apr-21 at 18:53:10 -;; Last-Mod: 16-Jul-26 at 17:06:05 by Mats Lidell +;; Last-Mod: 20-Jul-26 at 00:24:21 by Mats Lidell ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -85,62 +85,148 @@ See Emacs bug#74042 related to usage of texi2any." (hy-test-helpers:kill-buffer "*info*"))) (ert-deftest hypb--in-string-p () - "Verify basic quote handing by `hypb:in-string-p'." - (let ((s '(("\"str\"" . text-mode) ;; double-quotes: - ("'str'" . python-mode) ;; Python single-quotes: - ("'''str'''" . python-mode) ;; Python triple single-quotes: - ("\"\"\"str\"\"\"" . python-mode) ;; Python triple double-quotes: - ("``str''" . texinfo-mode))) ;; Texinfo open and close quotes: - (test-num 0) - str - mode) + "Verify basic quote handling by `hypb:in-string-p'. +Verify with and without caching." + (dolist (v '(nil t)) + (let ((s '((" \"str\" " . text-mode) ;; double-quotes: + (" 'str' " . python-mode) ;; Python single-quotes: + (" '''str''' " . python-mode) ;; Python triple single-quotes: + (" \"\"\"str\"\"\" " . python-mode) ;; Python triple double-quotes: + (" ``str'' " . texinfo-mode))) ;; Texinfo open and close quotes: + (test-num 0) + str + mode + (hypb:in-string-cache-disable v)) + (with-temp-buffer + (dolist (v s) + (setq str (car v) + mode (cdr v)) + (erase-buffer) + (funcall mode) + (insert str) + (let ((pos 0) + (response-list '(nil nil nil nil t t t nil nil nil nil))) + (dolist (response response-list) + (setq pos (1+ pos)) + (goto-char pos) + (if (not response) + (progn + (ert-info ((format "Test #%d: At pos %d, char '%c', expected outside string text \"%s\" in mode: %s" + test-num (point) (char-after (point)) str mode)) + (should-not (hypb:in-string-p)))) + (ert-info ((format "Test #%d: At pos %d, expected inside string text \"%s\" in mode: %s" + test-num (point) str mode)) + (should (hypb:in-string-p)) + (let ((seq (hypb:in-string-p nil t))) + (should (sequencep seq)) + (cl-destructuring-bind (val beg end) seq + (should (stringp val)) + (should (and beg end (= (- end beg) 3)))))))))))))) + +;; hypb:in-string-p tests with multiple strings +;; 'one string with "double \"quoted\" nested" that \' \' ends here'. +(defun hypb--gen-response-list (prefix q1 swq q2 suffix) + "Generate a response list from the prefix, suffix and string with quotes." + (append (make-list (length prefix) nil) + (make-list (length q1) nil) ;; Starting quote regarded as outside quote + (make-list (length swq) t) + (make-list (length q2) t) ;; Ending quote regarded as inside quote + (make-list (length suffix) nil))) + +(ert-deftest hypb--in-string-p--strings-with-quotes () + "Verify that strings containing quotes are identified. +The test string is built by concatenating prefix, mode-start-quote, +string-with-quotes, mode-end-quote, and suffix. Points within prefix +and suffix are checked to be outside of the string. Points within +string-with-quotes is checked to be inside of the string. For each test +string a list of mode settings that are applicable for that test string +are tried. If `hypb:in-string-p' is expected to see point as within +string is generated by `hypb--gen-response-list'." + (let ((prefix " pre ") + (suffix " suff ") + (mode-list '((txt . (text-mode "\"" "\"")) + (py1 . (python-mode "'" "'")) + (py2 . (python-mode "'''" "'''")) + (py3 . (python-mode "\"\"\"" "\"\"\"")) + (tex . (texinfo-mode "``" "''")))) + (swq-list + '(("word" . (txt py1 py2 py3 tex)) + ("wo'rd" . (txt py2 py3 tex)) + ("wo\"rd" . (py1 py2 py3 tex)) + (" \\\"quoted string\\\" " . (txt py1 py2 py3 tex)) + ("\\\"quoted string\\\"" . (txt py1 py2 py3 tex)) + (" \\\"quoted ' string\\\" " . (txt py2 py3 tex)) + (" 'quoted \\\" string' " . (txt py2 py3 tex)) + (" 'quoted string' " . (txt py2 py3 tex)) + (" 'quoted \\\"in quotes\\\" string' " . (txt py2 py3 tex)) + ("'quoted string'" . (txt py2 py3 tex)) + ("'quoted \\\"in quotes\\\" string'" . (txt py2 py3 tex)))) + (test-num 0)) (with-temp-buffer - (dolist (v s) - (setq str (car v) - mode (cdr v)) - (erase-buffer) - (funcall mode) - (insert str) - (goto-char (/ (length str) 2)) - (ert-info ((format "Test #%d: At pos %d, expected within \"%s\" in mode: %s" - test-num (point) str mode)) - (should (hypb:in-string-p)) - (let ((seq (hypb:in-string-p nil t))) - (should (sequencep seq)) - (cl-destructuring-bind (val beg end) seq - (should (stringp val)) - (should (and beg end (= (- end beg) 3)))))))))) + (dolist (swq-word swq-list) + (let ((swq (car swq-word)) + (modes (cdr swq-word))) + (dolist (m modes) + (let* ((mode (nth 0 (alist-get m mode-list))) + (quote1 (nth 1 (alist-get m mode-list))) + (quote2 (nth 2 (alist-get m mode-list))) + (s (concat prefix quote1 swq quote2 suffix))) + (setq test-num (1+ test-num)) + (erase-buffer) + (funcall mode) + (insert s) + (let ((pos 0) + (response-list (hypb--gen-response-list prefix quote1 swq quote2 suffix))) + (dolist (response response-list) + (setq pos (1+ pos)) + (goto-char pos) + (if (not response) + (progn + (ert-info ((format "Test #%d: At pos %d, char '%c', expected outside string text >|%s|< in mode: %s" + test-num (point) (char-after (point)) s mode)) + (should-not (hypb:in-string-p)))) + (ert-info ((format "Test #%d: At pos %d, char '%c', expected inside string text >|%s|< in mode: %s" + test-num (point) (char-after (point)) s mode)) + (should (hypb:in-string-p)) + (let ((seq (hypb:in-string-p nil t))) + (should (sequencep seq)) + (cl-destructuring-bind (val beg end) seq + (should (stringp val)) + (should (and beg end (= (- end beg) (length swq))))))))))))))))) (ert-deftest hypb--in-string-p--max-lines () - "Verify max lines handling by `hypb:in-string-p'." - (let* ((str "1\n\\\"2\n") - (range (list str 2 8))) - (with-temp-buffer - (insert (format "\"%s\"" str)) - (goto-line 1) (move-to-column 1) - ;; First line. Line starts with quote. - (should-not (hypb:in-string-p 1)) - (should (hypb:in-string-p 2)) - (should (hypb:in-string-p 3)) - (should (hypb:in-string-p 99)) - - ;; With range-flag - (should (equal range (hypb:in-string-p 2 t))) - (should (equal range (hypb:in-string-p 3 t))) - (should (equal range (hypb:in-string-p 99 t))) - - ;; Zero max-lines - (should-not (hypb:in-string-p 0)) - - ;; Second line. No quote on the line. - (goto-line 2) - (should-not (hypb:in-string-p 1)) - (should (hypb:in-string-p 2)) - (should (hypb:in-string-p 3)) - - ;; With range-flag - (should (equal range (hypb:in-string-p 2 t))) - (should (equal range (hypb:in-string-p 3 t)))))) + "Verify max lines handling by `hypb:in-string-p'. +Verify with and without caching." + (dolist (v '(nil t)) + (let* ((str "1\n\\\"2\n") + (range (list str 2 8)) + (hypb:in-string-cache-disable v)) + (with-temp-buffer + (insert (format "\"%s\"" str)) + (goto-line 1) (move-to-column 1) + ;; First line. Line starts with quote. + (should-not (hypb:in-string-p 1)) + (should (hypb:in-string-p 2)) + (should (hypb:in-string-p 3)) + (should (hypb:in-string-p 99)) + + ;; With range-flag + (should (equal range (hypb:in-string-p 2 t))) + (should (equal range (hypb:in-string-p 3 t))) + (should (equal range (hypb:in-string-p 99 t))) + + ;; Zero max-lines + (should-not (hypb:in-string-p 0)) + + ;; Second line. No quote on the line. + (goto-line 2) + (should-not (hypb:in-string-p 1)) + (should (hypb:in-string-p 2)) + (should (hypb:in-string-p 3)) + + ;; With range-flag + (should (equal range (hypb:in-string-p 2 t))) + (should (equal range (hypb:in-string-p 3 t))))))) (ert-deftest hypb--string-count-matches () "Verify `hypb--string-count-matches'."