summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test-custom-line-paragraph-duplicate-line-or-region.el463
1 files changed, 463 insertions, 0 deletions
diff --git a/tests/test-custom-line-paragraph-duplicate-line-or-region.el b/tests/test-custom-line-paragraph-duplicate-line-or-region.el
new file mode 100644
index 00000000..22f19c16
--- /dev/null
+++ b/tests/test-custom-line-paragraph-duplicate-line-or-region.el
@@ -0,0 +1,463 @@
+;;; test-custom-line-paragraph-duplicate-line-or-region.el --- Tests for cj/duplicate-line-or-region -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Tests for the cj/duplicate-line-or-region function from custom-line-paragraph.el
+;;
+;; This function duplicates the current line or active region below the original.
+;; When called with a prefix argument, the duplicated text is commented out.
+;;
+;; IMPORTANT NOTE ON REGION ACTIVATION IN BATCH MODE:
+;; When testing functions that use (region-active-p) in batch mode, you must
+;; explicitly activate the region. Unlike interactive Emacs, batch mode does
+;; not automatically activate regions when you set mark and point.
+;;
+;; To properly test region-based behavior in batch mode:
+;; 1. Enable transient-mark-mode: (transient-mark-mode 1)
+;; 2. Set mark and point as needed
+;; 3. Explicitly activate the mark: (activate-mark)
+
+;;; Code:
+
+(require 'ert)
+(require 'testutil-general)
+
+;; Add modules directory to load path
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+
+;; Stub dependencies before loading the module
+(defvar cj/custom-keymap (make-sparse-keymap)
+ "Stub keymap for testing.")
+
+;; Stub expand-region package
+(provide 'expand-region)
+
+;; Now load the actual production module
+(require 'custom-line-paragraph)
+
+;;; Setup and Teardown
+
+(defun test-duplicate-line-or-region-setup ()
+ "Setup for duplicate-line-or-region tests."
+ (cj/create-test-base-dir))
+
+(defun test-duplicate-line-or-region-teardown ()
+ "Teardown for duplicate-line-or-region tests."
+ (cj/delete-test-base-dir))
+
+;;; Normal Cases
+
+(ert-deftest test-duplicate-line-or-region-single-line-without-comment ()
+ "Should duplicate single line below original without commenting."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "line one")
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ (should (string-match-p "line one\nline one" (buffer-string))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-single-line-with-comment ()
+ "Should duplicate single line and comment the duplicate."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (emacs-lisp-mode) ; Enable comment syntax
+ (insert "line one")
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region t) ; Pass comment argument
+ (should (string-match-p "line one\n;; line one" (buffer-string))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-multi-line-region-without-comment ()
+ "Should duplicate entire region below without commenting."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "line one\nline two\nline three")
+ (goto-char (point-min))
+ (set-mark (point))
+ (goto-char (point-max))
+ (transient-mark-mode 1)
+ (activate-mark)
+ (cj/duplicate-line-or-region)
+ (should (string-match-p "line one\nline two\nline three\nline one\nline two\nline three" (buffer-string))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-multi-line-region-with-comment ()
+ "Should duplicate region and comment all duplicated lines."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (emacs-lisp-mode)
+ (insert "line one\nline two\nline three")
+ (goto-char (point-min))
+ (set-mark (point))
+ (goto-char (point-max))
+ (transient-mark-mode 1)
+ (activate-mark)
+ (cj/duplicate-line-or-region t)
+ ;; All duplicated lines should be commented
+ (should (string-match-p ";; line one\n;; line two\n;; line three" (buffer-string))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-cursor-position-unchanged ()
+ "Should keep cursor at original position (save-excursion)."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "line one\nline two")
+ (goto-char (point-min))
+ (forward-char 5) ; Position in middle of first line
+ (let ((original-pos (point)))
+ (cj/duplicate-line-or-region)
+ (should (= (point) original-pos))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-original-content-preserved ()
+ "Should preserve original text unchanged."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "original line")
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ ;; Original should still be there
+ (goto-char (point-min))
+ (should (looking-at "original line")))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-preserves-text-content ()
+ "Should exactly duplicate text content."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "exact text")
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ ;; Count occurrences of "exact text"
+ (should (= 2 (how-many "exact text" (point-min) (point-max)))))
+ (test-duplicate-line-or-region-teardown)))
+
+;;; Boundary Cases
+
+(ert-deftest test-duplicate-line-or-region-at-buffer-start ()
+ "Should handle duplication from beginning of buffer."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "first line\nsecond line")
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ (should (string-match-p "first line\nfirst line\nsecond line" (buffer-string))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-at-buffer-end ()
+ "Should handle duplication at end of buffer."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "first line\nlast line")
+ (goto-char (point-max))
+ (cj/duplicate-line-or-region)
+ (should (string-match-p "last line\nlast line$" (buffer-string))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-empty-line ()
+ "Should duplicate empty line."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "")
+ (cj/duplicate-line-or-region)
+ ;; Should have duplicated the empty content
+ (should (string= "\n" (buffer-string))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-only-whitespace ()
+ "Should preserve whitespace in duplicate."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert " ")
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ (should (string= " \n " (buffer-string))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-very-long-line ()
+ "Should handle very long lines (5000+ chars)."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (let ((long-line (make-string 5000 ?x)))
+ (insert long-line)
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ (should (= 2 (how-many long-line (point-min) (point-max))))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-region-with-empty-lines ()
+ "Should duplicate empty lines within region."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "line one\n\nline three")
+ (goto-char (point-min))
+ (set-mark (point))
+ (goto-char (point-max))
+ (transient-mark-mode 1)
+ (activate-mark)
+ (cj/duplicate-line-or-region)
+ ;; Should have empty line duplicated
+ (should (string-match-p "line one\n\nline three\nline one\n\nline three" (buffer-string))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-single-character ()
+ "Should handle minimal single character content."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "x")
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ (should (string= "x\nx" (buffer-string))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-unicode-emoji ()
+ "Should handle Unicode and emoji characters."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "hello 👋 café")
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ (should (string-match-p "hello 👋 café\nhello 👋 café" (buffer-string))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-with-tabs ()
+ "Should preserve tab characters."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "line\twith\ttabs")
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ (should (string-match-p "line\twith\ttabs\nline\twith\ttabs" (buffer-string))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-mixed-whitespace ()
+ "Should preserve exact whitespace."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert " line \t text ")
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ (should (string= " line \t text \n line \t text " (buffer-string))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-narrowed-buffer ()
+ "Should respect buffer narrowing."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "before\ntarget\nafter")
+ (goto-char (point-min))
+ (forward-line 1)
+ (let ((beg (point)))
+ (forward-line 1)
+ (narrow-to-region beg (point))
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ (widen)
+ ;; Should still have before and after
+ (should (string-match-p "before" (buffer-string)))
+ (should (string-match-p "after" (buffer-string)))
+ ;; Target should be duplicated
+ (should (= 2 (how-many "target" (point-min) (point-max))))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-backwards-region ()
+ "Should handle backwards region (mark after point)."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "line one\nline two")
+ (goto-char (point-max))
+ (set-mark (point))
+ (goto-char (point-min))
+ (transient-mark-mode 1)
+ (activate-mark)
+ (cj/duplicate-line-or-region)
+ (should (= 2 (how-many "line one" (point-min) (point-max))))
+ (should (= 2 (how-many "line two" (point-min) (point-max)))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-entire-buffer ()
+ "Should handle entire buffer selected as region."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "one\ntwo\nthree")
+ (transient-mark-mode 1)
+ (mark-whole-buffer)
+ (activate-mark)
+ (cj/duplicate-line-or-region)
+ (should (= 2 (how-many "one" (point-min) (point-max))))
+ (should (= 2 (how-many "two" (point-min) (point-max))))
+ (should (= 2 (how-many "three" (point-min) (point-max)))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-ending-mid-line ()
+ "Should handle region ending mid-line."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "line one\nline two\nline three")
+ (goto-char (point-min))
+ (forward-char 5) ; Middle of first line
+ (set-mark (point))
+ (forward-line 2)
+ (forward-char 5) ; Middle of third line
+ (transient-mark-mode 1)
+ (activate-mark)
+ (cj/duplicate-line-or-region)
+ ;; Should duplicate the selected portion
+ (should (> (length (buffer-string)) (length "line one\nline two\nline three"))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-trailing-whitespace ()
+ "Should preserve trailing whitespace."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "line with trailing ")
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ (should (string= "line with trailing \nline with trailing " (buffer-string))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-rtl-text ()
+ "Should handle RTL text."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "مرحبا")
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ (should (= 2 (how-many "مرحبا" (point-min) (point-max)))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-combining-characters ()
+ "Should handle Unicode combining characters."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "cafe\u0301") ; e with combining acute
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ (should (string-match-p "cafe\u0301\ncafe\u0301" (buffer-string))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-at-point-min ()
+ "Should handle duplication at point-min edge case."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "first")
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ (should (= 2 (how-many "first" (point-min) (point-max)))))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-at-point-max ()
+ "Should handle duplication at point-max edge case."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "last")
+ (goto-char (point-max))
+ (cj/duplicate-line-or-region)
+ (should (= 2 (how-many "last" (point-min) (point-max)))))
+ (test-duplicate-line-or-region-teardown)))
+
+;;; Error Cases
+
+(ert-deftest test-duplicate-line-or-region-read-only-buffer ()
+ "Should error when attempting to modify read-only buffer."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "read only line")
+ (goto-char (point-min))
+ (read-only-mode 1)
+ (should-error (cj/duplicate-line-or-region)))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-buffer-modified-flag ()
+ "Should set buffer modified flag."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "line")
+ (set-buffer-modified-p nil)
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ (should (buffer-modified-p)))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-undo-behavior ()
+ "Should support undo after duplication."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (let* ((temp-file (expand-file-name "test-undo-dup.txt" cj/test-base-dir))
+ (original-content "line one"))
+ ;; Create file with initial content
+ (with-temp-file temp-file
+ (insert original-content))
+ ;; Open file and test undo
+ (find-file temp-file)
+ (buffer-enable-undo)
+ ;; Establish undo history
+ (goto-char (point-min))
+ (insert " ")
+ (delete-char -1)
+ (undo-boundary)
+ (goto-char (point-min))
+ (let ((before-dup (buffer-string)))
+ (cj/duplicate-line-or-region)
+ (undo-boundary)
+ (let ((after-dup (buffer-string)))
+ (should-not (string= before-dup after-dup))
+ (undo)
+ (should (string= before-dup (buffer-string)))))
+ (kill-buffer (current-buffer)))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-comment-without-syntax ()
+ "Should error when comment requested but no comment syntax defined."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ ;; Fundamental mode has no comment syntax
+ (fundamental-mode)
+ (insert "line")
+ (goto-char (point-min))
+ ;; Should error when trying to comment without syntax
+ (should-error (cj/duplicate-line-or-region t)))
+ (test-duplicate-line-or-region-teardown)))
+
+(ert-deftest test-duplicate-line-or-region-special-characters ()
+ "Should handle control characters."
+ (test-duplicate-line-or-region-setup)
+ (unwind-protect
+ (with-temp-buffer
+ (insert "line\u000Cwith\u000Dcontrol")
+ (goto-char (point-min))
+ (cj/duplicate-line-or-region)
+ (should (string-match-p "line\u000Cwith\u000Dcontrol\nline\u000Cwith\u000Dcontrol" (buffer-string))))
+ (test-duplicate-line-or-region-teardown)))
+
+(provide 'test-custom-line-paragraph-duplicate-line-or-region)
+;;; test-custom-line-paragraph-duplicate-line-or-region.el ends here