diff options
| author | Craig Jennings <c@cjennings.net> | 2025-10-26 17:40:28 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2025-10-26 17:40:28 -0500 |
| commit | c700bc14f091a346351474ebce24aa32e0dca814 (patch) | |
| tree | 82556caf6206c8e3b5836556e4402888eca66dea /tests | |
| parent | 1a6365b94352c966a9d0bc63ccf9e7727ec2d6e1 (diff) | |
feat+test:custom-file-buffer: add tests and safety refactoring
Add 106 unit tests with full coverage for move-buffer-and-file (51 tests)
and rename-buffer-and-file (55 tests). Refactor both functions using
interactive/non-interactive split pattern for simpler testing and reusability.
Changes:
- Split cj/move-buffer-and-file and cj/rename-buffer-and-file into internal
implementations (cj/--*) and interactive wrappers
- Add ok-if-exists parameter with user confirmation to prevent data loss
- Fix bugs: return values, path expansion, string-match arg order, regex
- Add test utilities for proper buffer cleanup and isolation
- Document interactive/non-interactive pattern in quality-engineer.org
- Document error message testing guidelines in quality-engineer.org
All 106 tests passing.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test-custom-file-buffer-move-buffer-and-file.el | 936 | ||||
| -rw-r--r-- | tests/test-custom-file-buffer-rename-buffer-and-file.el | 939 |
2 files changed, 1875 insertions, 0 deletions
diff --git a/tests/test-custom-file-buffer-move-buffer-and-file.el b/tests/test-custom-file-buffer-move-buffer-and-file.el new file mode 100644 index 00000000..1fc16011 --- /dev/null +++ b/tests/test-custom-file-buffer-move-buffer-and-file.el @@ -0,0 +1,936 @@ +;;; test-custom-file-buffer-move-buffer-and-file.el --- Tests for cj/move-buffer-and-file -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for the cj/--move-buffer-and-file function from custom-file-buffer.el +;; +;; This is the internal (non-interactive) implementation that moves both the +;; current buffer and its visited file to a new directory. It handles trailing +;; slashes, preserves file content, updates the visited-file-name, and clears +;; the modified flag. The interactive wrapper cj/move-buffer-and-file handles +;; user prompting and delegates to this implementation. + +;;; 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 ps-print package +(provide 'ps-print) + +;; Now load the actual production module +(require 'custom-file-buffer) + +;;; Setup and Teardown + +(defun test-move-buffer-and-file-setup () + "Setup for move-buffer-and-file tests." + (cj/create-test-base-dir)) + +(defun test-move-buffer-and-file-teardown () + "Teardown for move-buffer-and-file tests." + ;; Kill all buffers visiting files in test directory + (dolist (buf (buffer-list)) + (when (buffer-file-name buf) + (when (string-prefix-p cj/test-base-dir (buffer-file-name buf)) + (with-current-buffer buf + (set-buffer-modified-p nil)) + (kill-buffer buf)))) + (cj/delete-test-base-dir)) + +;;; Normal Cases + +(ert-deftest test-move-buffer-and-file-simple-move-should-succeed () + "Should move file and buffer to new directory successfully." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (target-file (expand-file-name "test.txt" target-dir)) + (content "Test content")) + (with-temp-file source-file + (insert content)) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-updates-buffer-file-name () + "Should update buffer-file-name to new location." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (target-file (expand-file-name "test.txt" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (string= (buffer-file-name) target-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-preserves-content () + "Should preserve file content after move." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (content "Original content\nWith multiple lines\n")) + (with-temp-file source-file + (insert content)) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (string= (buffer-string) content)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-preserves-buffer-name () + "Should preserve buffer name after move." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "myfile.txt" source-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (should (string= (buffer-name) "myfile.txt")) + (cj/--move-buffer-and-file target-dir) + (should (string= (buffer-name) "myfile.txt")) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-clears-modified-flag () + "Should clear buffer modified flag after move." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (insert "modification") + (should (buffer-modified-p)) + (cj/--move-buffer-and-file target-dir) + (should-not (buffer-modified-p)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-returns-t-on-success () + "Should return t on successful move." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (should (eq t (cj/--move-buffer-and-file target-dir))) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-deletes-source-file () + "Should delete source file after move." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-creates-target-file () + "Should create file in target directory." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (target-file (expand-file-name "test.txt" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +;;; Boundary Cases - Path Handling + +(ert-deftest test-move-buffer-and-file-trailing-slash-should-strip () + "Should handle directory with trailing slash." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (target-file (expand-file-name "test.txt" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file (concat target-dir "/")) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-trailing-backslash-should-strip () + "Should handle directory with trailing backslash." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (target-file (expand-file-name "test.txt" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file (concat target-dir "\\")) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-no-trailing-slash-should-work () + "Should work with directory without trailing slash." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (target-file (expand-file-name "test.txt" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-deeply-nested-target () + "Should move to deeply nested target directory." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "a/b/c/d/target")) + (source-file (expand-file-name "test.txt" source-dir)) + (target-file (expand-file-name "test.txt" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-relative-path-should-work () + "Should resolve relative paths relative to file's directory." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (target-file (expand-file-name "test.txt" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + ;; Use "../target" to go up from source/ to target/ + (cj/--move-buffer-and-file "../target") + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +;;; Boundary Cases - Character Encoding + +(ert-deftest test-move-buffer-and-file-unicode-filename () + "Should handle Unicode characters in filename." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test-café.txt" source-dir)) + (target-file (expand-file-name "test-café.txt" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-unicode-directory () + "Should handle Unicode characters in directory name." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target-ñoño")) + (source-file (expand-file-name "test.txt" source-dir)) + (target-file (expand-file-name "test.txt" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-emoji-in-filename () + "Should handle emoji in filename." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test-🎉-file.txt" source-dir)) + (target-file (expand-file-name "test-🎉-file.txt" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-rtl-characters () + "Should handle RTL text in filename." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test-مرحبا.txt" source-dir)) + (target-file (expand-file-name "test-مرحبا.txt" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-spaces-in-filename () + "Should handle spaces in filename." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test file with spaces.txt" source-dir)) + (target-file (expand-file-name "test file with spaces.txt" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-special-chars-in-filename () + "Should handle special characters in filename." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test[file]-(1).txt" source-dir)) + (target-file (expand-file-name "test[file]-(1).txt" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +;;; Boundary Cases - File Naming + +(ert-deftest test-move-buffer-and-file-hidden-file () + "Should handle hidden files (starting with dot)." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name ".hidden" source-dir)) + (target-file (expand-file-name ".hidden" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-no-extension () + "Should handle files without extensions." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "README" source-dir)) + (target-file (expand-file-name "README" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-multiple-dots-in-name () + "Should handle multiple dots in filename." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "my.file.name.test.txt" source-dir)) + (target-file (expand-file-name "my.file.name.test.txt" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-single-char-filename () + "Should handle single character filenames." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "x" source-dir)) + (target-file (expand-file-name "x" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-very-long-filename () + "Should handle very long filenames." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (long-name (concat (make-string 200 ?x) ".txt")) + (source-file (expand-file-name long-name source-dir)) + (target-file (expand-file-name long-name target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-very-long-path () + "Should handle very long paths." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((long-dir (make-string 100 ?x)) + (source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory long-dir)) + (long-filename (concat (make-string 100 ?y) ".txt")) + (source-file (expand-file-name long-filename source-dir)) + (target-file (expand-file-name long-filename target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +;;; Boundary Cases - File Content + +(ert-deftest test-move-buffer-and-file-empty-file () + "Should move empty file successfully." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "empty.txt" source-dir)) + (target-file (expand-file-name "empty.txt" target-dir))) + (with-temp-file source-file) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (should (= 0 (buffer-size))) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-large-file () + "Should move large file successfully." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "large.txt" source-dir)) + (large-content (make-string 100000 ?x))) + (with-temp-file source-file + (insert large-content)) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (string= (buffer-string) large-content)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-binary-file () + "Should move binary-like content successfully." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "binary.dat" source-dir)) + (target-file (expand-file-name "binary.dat" target-dir)) + (binary-content (string 0 1 2 3 255 254 253))) + (with-temp-file source-file + (set-buffer-multibyte nil) + (insert binary-content)) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-preserves-newlines () + "Should preserve different newline types." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "newlines.txt" source-dir)) + (content "Line 1\nLine 2\n\nLine 4\n")) + (with-temp-file source-file + (insert content)) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (string= (buffer-string) content)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-preserves-encoding () + "Should preserve UTF-8 encoded content." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "utf8.txt" source-dir)) + (content "Hello 世界 مرحبا Привет")) + (with-temp-file source-file + (insert content)) + (find-file source-file) + (cj/--move-buffer-and-file target-dir) + (should (string= (buffer-string) content)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +;;; Boundary Cases - Buffer State + +(ert-deftest test-move-buffer-and-file-with-unsaved-changes () + "Should handle buffer with unsaved changes." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (target-file (expand-file-name "test.txt" target-dir)) + (original "original")) + (with-temp-file source-file + (insert original)) + (find-file source-file) + (insert " modified") + (should (buffer-modified-p)) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (should-not (buffer-modified-p)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-with-multiple-windows () + "Should work when buffer is displayed in multiple windows." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (target-file (expand-file-name "test.txt" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (delete-other-windows) + (split-window) + (other-window 1) + (switch-to-buffer (get-file-buffer source-file)) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (kill-buffer (current-buffer)) + (delete-other-windows)) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-preserves-point-position () + "Should preserve point position in buffer." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (content "Line 1\nLine 2\nLine 3\n")) + (with-temp-file source-file + (insert content)) + (find-file source-file) + (goto-char (point-min)) + (forward-line 1) + (let ((original-point (point))) + (cj/--move-buffer-and-file target-dir) + (should (= (point) original-point))) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-preserves-mark () + "Should preserve mark in buffer." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (content "Line 1\nLine 2\nLine 3\n")) + (with-temp-file source-file + (insert content)) + (find-file source-file) + (goto-char (point-min)) + (set-mark (point)) + (forward-line 2) + (let ((original-mark (mark))) + (cj/--move-buffer-and-file target-dir) + (should (= (mark) original-mark))) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +;;; Error Cases - Buffer Issues + +(ert-deftest test-move-buffer-and-file-non-file-buffer-returns-nil () + "Should return nil when buffer not visiting a file." + (test-move-buffer-and-file-setup) + (unwind-protect + (let ((target-dir (cj/create-test-subdirectory "target"))) + (with-temp-buffer + (rename-buffer "non-file-buffer" t) + (let ((result (cj/--move-buffer-and-file target-dir))) + (should-not result)))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-scratch-buffer-returns-nil () + "Should return nil for scratch buffer." + (test-move-buffer-and-file-setup) + (unwind-protect + (let ((target-dir (cj/create-test-subdirectory "target"))) + (with-current-buffer "*scratch*" + (let ((result (cj/--move-buffer-and-file target-dir))) + (should-not result)))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-killed-buffer-should-error () + "Should error when operating on killed buffer." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (buf nil)) + (with-temp-file source-file + (insert "content")) + (setq buf (find-file source-file)) + (kill-buffer buf) + (should-error + (with-current-buffer buf + (cj/--move-buffer-and-file target-dir)))) + (test-move-buffer-and-file-teardown))) + +;;; Error Cases - Directory Issues + +(ert-deftest test-move-buffer-and-file-nonexistent-target-should-error () + "Should error when target directory doesn't exist." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (source-file (expand-file-name "test.txt" source-dir)) + (nonexistent-dir (expand-file-name "nonexistent" cj/test-base-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (should-error (cj/--move-buffer-and-file nonexistent-dir)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-target-is-file-not-dir-should-error () + "Should error when target is a file, not directory." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (source-file (expand-file-name "test.txt" source-dir)) + (target-file (expand-file-name "notadir.txt" cj/test-base-dir))) + (with-temp-file target-file + (insert "I'm a file")) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (should-error (cj/--move-buffer-and-file target-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-nil-directory-should-error () + "Should error when directory is nil." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (source-file (expand-file-name "test.txt" source-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (should-error (cj/--move-buffer-and-file nil)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-empty-string-directory-should-error () + "Should error when directory is empty string." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (source-file (expand-file-name "test.txt" source-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (should-error (cj/--move-buffer-and-file "")) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +;;; Error Cases - Permission Issues + +(ert-deftest test-move-buffer-and-file-no-read-permission-source-should-error () + "Should error when source file is not readable." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (set-file-modes source-file #o000) + (should-error (cj/--move-buffer-and-file target-dir)) + (set-file-modes source-file #o644) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-no-write-permission-target-should-error () + "Should error when target directory is not writable." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir))) + (with-temp-file source-file + (insert "content")) + (set-file-modes target-dir #o555) + (find-file source-file) + (should-error (cj/--move-buffer-and-file target-dir)) + (set-file-modes target-dir #o755) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-no-delete-permission-source-should-error () + "Should error when source directory doesn't allow deletion." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (set-file-modes source-dir #o555) + (should-error (cj/--move-buffer-and-file target-dir)) + (set-file-modes source-dir #o755) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +;;; Error Cases - File Conflicts + +(ert-deftest test-move-buffer-and-file-target-exists-should-overwrite () + "Should overwrite existing file when ok-if-exists is t." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (target-file (expand-file-name "test.txt" target-dir)) + (new-content "New content") + (old-content "Old content")) + (with-temp-file target-file + (insert old-content)) + (with-temp-file source-file + (insert new-content)) + (find-file source-file) + (cj/--move-buffer-and-file target-dir t) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (revert-buffer t t) + (should (string= (buffer-string) new-content)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-target-exists-should-error-if-not-ok () + "Should error when target exists and ok-if-exists is nil." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (target-file (expand-file-name "test.txt" target-dir))) + (with-temp-file target-file + (insert "existing")) + (with-temp-file source-file + (insert "new")) + (find-file source-file) + (should-error (cj/--move-buffer-and-file target-dir nil)) + ;; Source should still exist since move failed + (should (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-interactive-prompts-if-target-exists () + "Should prompt user when called interactively and target exists." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (target-file (expand-file-name "test.txt" target-dir)) + (prompted nil)) + (with-temp-file target-file + (insert "existing")) + (with-temp-file source-file + (insert "new")) + (find-file source-file) + ;; Mock yes-or-no-p to capture that it was called + (cl-letf (((symbol-function 'yes-or-no-p) + (lambda (prompt) + (setq prompted t) + t)) + ((symbol-function 'read-directory-name) + (lambda (&rest _) target-dir))) + (call-interactively #'cj/move-buffer-and-file) + (should prompted)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-interactive-no-prompt-if-target-missing () + "Should not prompt when called interactively if target doesn't exist." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (prompted nil)) + (with-temp-file source-file + (insert "new")) + (find-file source-file) + ;; Mock yes-or-no-p to capture if it was called + (cl-letf (((symbol-function 'yes-or-no-p) + (lambda (prompt) + (setq prompted t) + t)) + ((symbol-function 'read-directory-name) + (lambda (&rest _) target-dir))) + (call-interactively #'cj/move-buffer-and-file) + (should-not prompted)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-source-deleted-during-operation-should-error () + "Should error if source file is deleted during operation." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (delete-file source-file) + (should-error (cj/--move-buffer-and-file target-dir)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +;;; Error Cases - Edge Cases + +(ert-deftest test-move-buffer-and-file-symlink-source-should-handle () + "Should handle symbolic link as source." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (real-file (expand-file-name "real.txt" source-dir)) + (symlink (expand-file-name "link.txt" source-dir)) + (target-file (expand-file-name "link.txt" target-dir))) + (with-temp-file real-file + (insert "content")) + (make-symbolic-link real-file symlink) + (find-file symlink) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(ert-deftest test-move-buffer-and-file-read-only-buffer-should-still-work () + "Should work even if buffer is read-only." + (test-move-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (source-file (expand-file-name "test.txt" source-dir)) + (target-file (expand-file-name "test.txt" target-dir))) + (with-temp-file source-file + (insert "content")) + (find-file source-file) + (read-only-mode 1) + (cj/--move-buffer-and-file target-dir) + (should (file-exists-p target-file)) + (should-not (file-exists-p source-file)) + (kill-buffer (current-buffer))) + (test-move-buffer-and-file-teardown))) + +(provide 'test-custom-file-buffer-move-buffer-and-file) +;;; test-custom-file-buffer-move-buffer-and-file.el ends here diff --git a/tests/test-custom-file-buffer-rename-buffer-and-file.el b/tests/test-custom-file-buffer-rename-buffer-and-file.el new file mode 100644 index 00000000..ca8acff8 --- /dev/null +++ b/tests/test-custom-file-buffer-rename-buffer-and-file.el @@ -0,0 +1,939 @@ +;;; test-custom-file-buffer-rename-buffer-and-file.el --- Tests for cj/--rename-buffer-and-file -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for the cj/--rename-buffer-and-file function from custom-file-buffer.el +;; +;; This is the internal (non-interactive) implementation that renames both the +;; current buffer and its visited file. The interactive wrapper +;; cj/rename-buffer-and-file handles user prompting and delegates to this +;; implementation. + +;;; 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 ps-print package +(provide 'ps-print) + +;; Now load the actual production module +(require 'custom-file-buffer) + +;;; Setup and Teardown + +(defun test-rename-buffer-and-file-setup () + "Setup for rename-buffer-and-file tests." + (cj/create-test-base-dir)) + +(defun test-rename-buffer-and-file-teardown () + "Teardown for rename-buffer-and-file tests." + ;; Kill all buffers visiting files in test directory + (dolist (buf (buffer-list)) + (when (buffer-file-name buf) + (when (string-prefix-p cj/test-base-dir (buffer-file-name buf)) + (with-current-buffer buf + (set-buffer-modified-p nil)) + (kill-buffer buf)))) + (cj/delete-test-base-dir)) + +;;; Normal Cases + +(ert-deftest test-rename-buffer-and-file-simple-rename () + "Should rename file in same directory." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name "new.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file "new.txt") + (should (file-exists-p new-file)) + (should-not (file-exists-p old-file)) + (should (string= (buffer-name) "new.txt")) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-different-directory () + "Should rename to absolute path in different directory." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (old-file (expand-file-name "file.txt" source-dir)) + (new-file (expand-file-name "renamed.txt" target-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file new-file) + (should (file-exists-p new-file)) + (should-not (file-exists-p old-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-different-extension () + "Should change file extension." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "file.txt" test-dir)) + (new-file (expand-file-name "file.md" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file "file.md") + (should (file-exists-p new-file)) + (should-not (file-exists-p old-file)) + (should (string= (buffer-name) "file.md")) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-preserves-content () + "Should preserve file content after rename." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (content "Important content\nWith multiple lines")) + (with-temp-file old-file + (insert content)) + (find-file old-file) + (cj/--rename-buffer-and-file "new.txt") + (should (string= (buffer-string) content)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-updates-buffer-name () + "Should update buffer name to match new filename." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (should (string= (buffer-name) "old.txt")) + (cj/--rename-buffer-and-file "new.txt") + (should (string= (buffer-name) "new.txt")) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-updates-buffer-file-name () + "Should update buffer-file-name correctly." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name "new.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file "new.txt") + (should (string= (buffer-file-name) new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-clears-modified-flag () + "Should clear modified flag after rename." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (insert "modification") + (should (buffer-modified-p)) + (cj/--rename-buffer-and-file "new.txt") + (should-not (buffer-modified-p)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-returns-t-on-success () + "Should return t when successful." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (should (eq t (cj/--rename-buffer-and-file "new.txt"))) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +;;; Boundary Cases - Naming + +(ert-deftest test-rename-buffer-and-file-unicode-in-name () + "Should handle Unicode characters in name." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name "café.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file "café.txt") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-emoji-in-name () + "Should handle emoji characters in name." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name "test-🎉.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file "test-🎉.txt") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-rtl-text-in-name () + "Should handle RTL text in name." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name "مرحبا.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file "مرحبا.txt") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-spaces-in-name () + "Should handle spaces in name." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name "my new file.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file "my new file.txt") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-special-chars-in-name () + "Should handle special characters in name." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name "[test]-(1).txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file "[test]-(1).txt") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-very-long-name () + "Should handle very long filename." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (long-name (concat (make-string 200 ?x) ".txt")) + (new-file (expand-file-name long-name test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file long-name) + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-single-char-name () + "Should handle single character name." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name "x" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file "x") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-multiple-dots () + "Should handle multiple dots in name." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name "my.file.name.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file "my.file.name.txt") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-no-extension () + "Should handle files without extension." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name "README" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file "README") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-hidden-file () + "Should handle hidden files (starting with dot)." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name ".hidden" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file ".hidden") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-trailing-whitespace () + "Should handle trailing/leading spaces in name." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name " spaced " test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file " spaced ") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-only-changes-case () + "Should handle case-only rename on case-sensitive filesystems." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "test.txt" test-dir)) + (new-file (expand-file-name "TEST.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + ;; On case-insensitive systems, need ok-if-exists + (cj/--rename-buffer-and-file "TEST.txt" t) + (should (string= (buffer-name) "TEST.txt")) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-adds-extension () + "Should handle adding extension to file." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "file" test-dir)) + (new-file (expand-file-name "file.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file "file.txt") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-removes-extension () + "Should handle removing extension from file." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "file.txt" test-dir)) + (new-file (expand-file-name "file" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file "file") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-just-extension () + "Should handle name that is just extension." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name ".gitignore" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file ".gitignore") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +;;; Boundary Cases - Path Handling + +(ert-deftest test-rename-buffer-and-file-relative-path () + "Should handle relative path." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (old-file (expand-file-name "file.txt" source-dir)) + (new-file (expand-file-name "renamed.txt" target-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file "../target/renamed.txt") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-absolute-path () + "Should handle absolute path." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (old-file (expand-file-name "file.txt" source-dir)) + (new-file (expand-file-name "renamed.txt" target-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file new-file) + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-parent-directory () + "Should handle parent directory reference." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((parent-dir (cj/create-test-subdirectory "parent")) + (source-dir (cj/create-test-subdirectory "parent/source")) + (old-file (expand-file-name "file.txt" source-dir)) + (new-file (expand-file-name "renamed.txt" parent-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file "../renamed.txt") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-deeply-nested-target () + "Should handle deeply nested target directory." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "a/b/c/d/target")) + (old-file (expand-file-name "file.txt" source-dir)) + (new-file (expand-file-name "renamed.txt" target-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file new-file) + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-same-directory-basename-only () + "Should rename in same directory using just basename." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name "new.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file "new.txt") + (should (file-exists-p new-file)) + (should-not (file-exists-p old-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-expand-tilde () + "Should expand tilde in path." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + ;; Use a path relative to home that we can create + (home-test-dir (expand-file-name "temp-test-rename" "~")) + (new-file (expand-file-name "renamed.txt" home-test-dir))) + (make-directory home-test-dir t) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (cj/--rename-buffer-and-file (concat "~/temp-test-rename/renamed.txt")) + (should (file-exists-p new-file)) + (kill-buffer (current-buffer)) + (delete-directory home-test-dir t)) + (test-rename-buffer-and-file-teardown))) + +;;; Boundary Cases - File Content + +(ert-deftest test-rename-buffer-and-file-empty-file () + "Should handle empty file." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name "new.txt" test-dir))) + (with-temp-file old-file) + (find-file old-file) + (cj/--rename-buffer-and-file "new.txt") + (should (file-exists-p new-file)) + (should (= 0 (buffer-size))) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-large-file () + "Should handle large file." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (large-content (make-string 100000 ?x))) + (with-temp-file old-file + (insert large-content)) + (find-file old-file) + (cj/--rename-buffer-and-file "new.txt") + (should (string= (buffer-string) large-content)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-binary-content () + "Should handle binary content." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.dat" test-dir)) + (new-file (expand-file-name "new.dat" test-dir)) + (binary-content (string 0 1 2 3 255 254 253))) + (with-temp-file old-file + (set-buffer-multibyte nil) + (insert binary-content)) + (find-file old-file) + (cj/--rename-buffer-and-file "new.dat") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-preserves-newlines () + "Should preserve different newline types." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (content "Line 1\nLine 2\n\nLine 4\n")) + (with-temp-file old-file + (insert content)) + (find-file old-file) + (cj/--rename-buffer-and-file "new.txt") + (should (string= (buffer-string) content)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-preserves-encoding () + "Should preserve UTF-8 encoded content." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (content "Hello 世界 مرحبا Привет")) + (with-temp-file old-file + (insert content)) + (find-file old-file) + (cj/--rename-buffer-and-file "new.txt") + (should (string= (buffer-string) content)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +;;; Boundary Cases - Buffer State + +(ert-deftest test-rename-buffer-and-file-with-unsaved-changes () + "Should handle buffer with unsaved changes." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir))) + (with-temp-file old-file + (insert "original")) + (find-file old-file) + (insert " modified") + (should (buffer-modified-p)) + (cj/--rename-buffer-and-file "new.txt") + (should-not (buffer-modified-p)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-multiple-windows () + "Should work when buffer displayed in multiple windows." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (delete-other-windows) + (split-window) + (other-window 1) + (switch-to-buffer (get-file-buffer old-file)) + (cj/--rename-buffer-and-file "new.txt") + (should (string= (buffer-name) "new.txt")) + (kill-buffer (current-buffer)) + (delete-other-windows)) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-preserves-point () + "Should preserve point position." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (content "Line 1\nLine 2\nLine 3\n")) + (with-temp-file old-file + (insert content)) + (find-file old-file) + (goto-char (point-min)) + (forward-line 1) + (let ((original-point (point))) + (cj/--rename-buffer-and-file "new.txt") + (should (= (point) original-point))) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-preserves-mark () + "Should preserve mark." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (content "Line 1\nLine 2\nLine 3\n")) + (with-temp-file old-file + (insert content)) + (find-file old-file) + (goto-char (point-min)) + (set-mark (point)) + (forward-line 2) + (let ((original-mark (mark))) + (cj/--rename-buffer-and-file "new.txt") + (should (= (mark) original-mark))) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-read-only-buffer () + "Should work even with read-only buffer." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name "new.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (read-only-mode 1) + (cj/--rename-buffer-and-file "new.txt") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +;;; Error Cases - Buffer Issues + +(ert-deftest test-rename-buffer-and-file-non-file-buffer-returns-nil () + "Should return nil when buffer not visiting file." + (test-rename-buffer-and-file-setup) + (unwind-protect + (with-temp-buffer + (rename-buffer "non-file-buffer" t) + (let ((result (cj/--rename-buffer-and-file "new.txt"))) + (should-not result))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-scratch-buffer-returns-nil () + "Should return nil for scratch buffer." + (test-rename-buffer-and-file-setup) + (unwind-protect + (with-current-buffer "*scratch*" + (let ((result (cj/--rename-buffer-and-file "new.txt"))) + (should-not result))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-buffer-name-exists-should-error () + "Should error when buffer with new name exists." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (file1 (expand-file-name "file1.txt" test-dir)) + (file2 (expand-file-name "file2.txt" test-dir))) + (with-temp-file file1 + (insert "content1")) + (with-temp-file file2 + (insert "content2")) + (find-file file1) + (let ((buf1 (current-buffer))) + (find-file file2) + ;; Try to rename file2 to file1.txt (buffer exists) + (should-error (cj/--rename-buffer-and-file "file1.txt")) + (kill-buffer (current-buffer)) + (kill-buffer buf1))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-killed-buffer-should-error () + "Should error when operating on killed buffer." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (buf nil)) + (with-temp-file old-file + (insert "content")) + (setq buf (find-file old-file)) + (kill-buffer buf) + (should-error + (with-current-buffer buf + (cj/--rename-buffer-and-file "new.txt")))) + (test-rename-buffer-and-file-teardown))) + +;;; Error Cases - File Conflicts + +(ert-deftest test-rename-buffer-and-file-target-exists-should-error-if-not-ok () + "Should error when target exists and ok-if-exists is nil." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name "new.txt" test-dir))) + (with-temp-file old-file + (insert "old content")) + (with-temp-file new-file + (insert "existing content")) + (find-file old-file) + (should-error (cj/--rename-buffer-and-file "new.txt" nil)) + ;; Old file should still exist since rename failed + (should (file-exists-p old-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-target-exists-should-overwrite-if-ok () + "Should overwrite when target exists and ok-if-exists is t." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name "new.txt" test-dir)) + (old-content "old content") + (new-content "existing content")) + (with-temp-file old-file + (insert old-content)) + (with-temp-file new-file + (insert new-content)) + (find-file old-file) + (cj/--rename-buffer-and-file "new.txt" t) + (should (file-exists-p new-file)) + (should-not (file-exists-p old-file)) + ;; Content should be from old file + (revert-buffer t t) + (should (string= (buffer-string) old-content)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-source-deleted-should-error () + "Should error if source file deleted during operation." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (delete-file old-file) + (should-error (cj/--rename-buffer-and-file "new.txt")) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-same-name-is-noop () + "Should handle rename to same name as no-op." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "file.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + ;; Rename to same name with ok-if-exists + (cj/--rename-buffer-and-file "file.txt" t) + (should (file-exists-p old-file)) + (should (string= (buffer-name) "file.txt")) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +;;; Error Cases - Path Issues + +(ert-deftest test-rename-buffer-and-file-nil-name-should-error () + "Should error when new-name is nil." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (should-error (cj/--rename-buffer-and-file nil)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-empty-name-should-error () + "Should error when new-name is empty string." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (should-error (cj/--rename-buffer-and-file "")) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-nonexistent-target-dir-should-error () + "Should error when target directory doesn't exist." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (nonexistent-path (expand-file-name "nonexistent/new.txt" cj/test-base-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (should-error (cj/--rename-buffer-and-file nonexistent-path)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-target-is-directory-should-error () + "Should error when new-name is existing directory." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (target-dir (cj/create-test-subdirectory "target")) + (old-file (expand-file-name "old.txt" test-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (should-error (cj/--rename-buffer-and-file target-dir)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +;;; Error Cases - Permissions + +(ert-deftest test-rename-buffer-and-file-no-write-permission-target () + "Should error when target directory not writable." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (old-file (expand-file-name "old.txt" source-dir)) + (new-file (expand-file-name "new.txt" target-dir))) + (with-temp-file old-file + (insert "content")) + (set-file-modes target-dir #o555) + (find-file old-file) + (should-error (cj/--rename-buffer-and-file new-file)) + (set-file-modes target-dir #o755) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-no-delete-permission-source-dir () + "Should error when source directory doesn't allow deletion." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((source-dir (cj/create-test-subdirectory "source")) + (target-dir (cj/create-test-subdirectory "target")) + (old-file (expand-file-name "old.txt" source-dir)) + (new-file (expand-file-name "new.txt" target-dir))) + (with-temp-file old-file + (insert "content")) + (find-file old-file) + (set-file-modes source-dir #o555) + (should-error (cj/--rename-buffer-and-file new-file)) + (set-file-modes source-dir #o755) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +;;; Error Cases - Edge Cases + +(ert-deftest test-rename-buffer-and-file-symlink-source () + "Should handle symbolic link as source." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (real-file (expand-file-name "real.txt" test-dir)) + (symlink (expand-file-name "link.txt" test-dir)) + (new-file (expand-file-name "renamed.txt" test-dir))) + (with-temp-file real-file + (insert "content")) + (make-symbolic-link real-file symlink) + (find-file symlink) + (cj/--rename-buffer-and-file "renamed.txt") + (should (file-exists-p new-file)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(ert-deftest test-rename-buffer-and-file-interactive-prompts-on-conflict () + "Should prompt user when called interactively and file exists." + (test-rename-buffer-and-file-setup) + (unwind-protect + (let* ((test-dir (cj/create-test-subdirectory "test")) + (old-file (expand-file-name "old.txt" test-dir)) + (new-file (expand-file-name "new.txt" test-dir)) + (prompted nil)) + (with-temp-file old-file + (insert "old")) + (with-temp-file new-file + (insert "existing")) + (find-file old-file) + ;; Mock yes-or-no-p to capture that it was called + (cl-letf (((symbol-function 'yes-or-no-p) + (lambda (prompt) + (setq prompted t) + t)) + ((symbol-function 'read-string) + (lambda (&rest _) "new.txt"))) + (call-interactively #'cj/rename-buffer-and-file) + (should prompted)) + (kill-buffer (current-buffer))) + (test-rename-buffer-and-file-teardown))) + +(provide 'test-custom-file-buffer-rename-buffer-and-file) +;;; test-custom-file-buffer-rename-buffer-and-file.el ends here |
