Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 140 additions & 54 deletions test/hypb-tests.el
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
;; Author: Mats Lidell <matsl@gnu.org>
;;
;; 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
;;
Expand Down Expand Up @@ -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'."
Expand Down
Loading