From 9206de07d7bb1e549aacada300f637aee1b620e9 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Tue, 5 May 2026 14:10:30 -0500 Subject: test: cover --setup-display, --restore-display, --migrate-from-source, scope=directory I added unit tests for the display-state helpers (capture text scale, variable-pitch, modeline; restore them on session exit), the directory branch of `org-drill-current-scope', and `--migrate-from-source''s three-branch cond (matching ID, no ID, ignore-new-items). Coverage moved from 81.8% to 82.4%. --- tests/test-org-drill-display-state.el | 115 ++++++++++++++++++++++++++++ tests/test-org-drill-migrate-from-source.el | 99 ++++++++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 tests/test-org-drill-display-state.el create mode 100644 tests/test-org-drill-migrate-from-source.el diff --git a/tests/test-org-drill-display-state.el b/tests/test-org-drill-display-state.el new file mode 100644 index 0000000..baad0a3 --- /dev/null +++ b/tests/test-org-drill-display-state.el @@ -0,0 +1,115 @@ +;;; test-org-drill-display-state.el --- Tests for display-state save/restore -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for `org-drill--setup-display' and `org-drill--restore-display', +;; which save the user's text scale, variable-pitch mode, and modeline at +;; session start and put them back at session end. Also covers the +;; `directory' branch of `org-drill-current-scope'. + +;;; Code: + +(require 'ert) +(require 'cl-lib) +(require 'org) +(require 'org-drill) + +;;;; --save-display-state / --restore-display + +(ert-deftest test-save-display-state-records-current-buffer () + "After save, `org-drill--saved-display-buffer' holds the buffer." + (with-temp-buffer + (let ((org-drill-text-size-during-session nil) + (org-drill-use-variable-pitch nil) + (org-drill-hide-modeline-during-session nil) + (org-drill--saved-display-buffer nil)) + (org-drill--setup-display) + (should (eq (current-buffer) org-drill--saved-display-buffer))))) + +(ert-deftest test-save-display-state-skips-when-flags-nil () + "With every flag nil, save records buffer but doesn't capture face/mode state." + (with-temp-buffer + (let ((org-drill-text-size-during-session nil) + (org-drill-use-variable-pitch nil) + (org-drill-hide-modeline-during-session nil) + (org-drill--saved-display-buffer nil) + (org-drill--saved-text-scale 'sentinel) + (org-drill--saved-variable-pitch-mode 'unbound) + (org-drill--saved-modeline-format 'sentinel)) + (org-drill--setup-display) + ;; Saved-text-scale is left untouched because the flag was nil. + (should (eq 'sentinel org-drill--saved-text-scale)) + (should (eq 'unbound org-drill--saved-variable-pitch-mode)) + (should (eq 'sentinel org-drill--saved-modeline-format))))) + +(ert-deftest test-save-display-state-captures-modeline-when-hide-flag-set () + "With `org-drill-hide-modeline-during-session' t, the modeline is saved and cleared." + (with-temp-buffer + (let ((org-drill-text-size-during-session nil) + (org-drill-use-variable-pitch nil) + (org-drill-hide-modeline-during-session t) + (org-drill--saved-display-buffer nil) + (org-drill--saved-modeline-format nil)) + (setq-local mode-line-format "marker-mode-line") + (org-drill--setup-display) + (should (equal "marker-mode-line" org-drill--saved-modeline-format)) + (should (null mode-line-format))))) + +(ert-deftest test-restore-display-puts-modeline-back () + "Restore sets mode-line-format on the saved-display-buffer back to its prior value." + (with-temp-buffer + (let ((org-drill-text-size-during-session nil) + (org-drill-use-variable-pitch nil) + (org-drill-hide-modeline-during-session t) + (org-drill--saved-display-buffer (current-buffer)) + (org-drill--saved-text-scale nil) + (org-drill--saved-variable-pitch-mode 'unbound) + (org-drill--saved-modeline-format "saved-modeline")) + (setq-local mode-line-format nil) + (org-drill--restore-display) + (should (equal "saved-modeline" mode-line-format)) + (should (null org-drill--saved-modeline-format)) + (should (null org-drill--saved-display-buffer))))) + +(ert-deftest test-restore-display-handles-killed-buffer-target () + "If the saved-display-buffer was killed, restore must not crash." + (let ((tmp (generate-new-buffer " *drill-display-tmp*"))) + (let ((org-drill-text-size-during-session nil) + (org-drill-use-variable-pitch nil) + (org-drill-hide-modeline-during-session t) + (org-drill--saved-display-buffer tmp) + (org-drill--saved-text-scale nil) + (org-drill--saved-variable-pitch-mode 'unbound) + (org-drill--saved-modeline-format "saved-modeline")) + (kill-buffer tmp) + (org-drill--restore-display) + (should (null org-drill--saved-display-buffer))))) + +;;;; org-drill-current-scope (directory branch) + +(ert-deftest test-org-drill-current-scope-directory-returns-org-files-list () + "The `directory' scope expands to a list of .org files in the buffer's directory." + (let* ((tmpdir (make-temp-file "org-drill-scope-" t)) + (a (expand-file-name "a.org" tmpdir)) + (b (expand-file-name "b.org" tmpdir)) + (skip (expand-file-name ".hidden.org" tmpdir)) + (other (expand-file-name "c.txt" tmpdir))) + (unwind-protect + (progn + (write-region "" nil a) + (write-region "" nil b) + (write-region "" nil skip) + (write-region "" nil other) + (with-current-buffer (find-file-noselect a) + (let ((files (org-drill-current-scope 'directory))) + (should (member a files)) + (should (member b files)) + (should-not (member skip files)) + (should-not (member other files))))) + (when (file-exists-p a) (delete-file a)) + (when (file-exists-p b) (delete-file b)) + (when (file-exists-p skip) (delete-file skip)) + (when (file-exists-p other) (delete-file other)) + (delete-directory tmpdir)))) + +(provide 'test-org-drill-display-state) +;;; test-org-drill-display-state.el ends here diff --git a/tests/test-org-drill-migrate-from-source.el b/tests/test-org-drill-migrate-from-source.el new file mode 100644 index 0000000..7b3d4ab --- /dev/null +++ b/tests/test-org-drill-migrate-from-source.el @@ -0,0 +1,99 @@ +;;; test-org-drill-migrate-from-source.el --- Tests for --migrate-from-source -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for `org-drill--migrate-from-source', which walks SRC and copies +;; scheduling data into matching DEST entries (matched by ID). The cond +;; has three branches: +;; +;; - SRC entry has no ID, or isn't a drill entry → skip +;; - SRC entry's ID matches a DEST ID → copy scheduling, drop from table +;; - SRC entry has an ID with no DEST match → either copy as new item, or +;; skip when ignore-new-items-p is t + +;;; Code: + +(require 'ert) +(require 'cl-lib) +(require 'org) +(require 'org-drill) + +(defmacro with-two-org-tempfiles (src-content dest-content &rest body) + "Run BODY with SRC-BUF and DEST-BUF bound to org-mode buffers. +SRC-CONTENT is loaded into SRC-BUF, DEST-CONTENT into DEST-BUF." + (declare (indent 2)) + `(let ((src-file (make-temp-file "org-drill-mig-src-" nil ".org")) + (dest-file (make-temp-file "org-drill-mig-dst-" nil ".org")) + src-buf dest-buf) + (unwind-protect + (progn + (setq src-buf (find-file-noselect src-file)) + (setq dest-buf (find-file-noselect dest-file)) + (with-current-buffer src-buf + (let ((org-startup-folded nil)) + (insert ,src-content) + (org-mode))) + (with-current-buffer dest-buf + (let ((org-startup-folded nil)) + (insert ,dest-content) + (org-mode))) + ,@body) + (when (file-exists-p src-file) (delete-file src-file)) + (when (file-exists-p dest-file) (delete-file dest-file))))) + +(ert-deftest test-migrate-matching-id-copies-scheduling () + "When SRC has an ID matching DEST, scheduling lands on DEST." + (with-two-org-tempfiles + "* Card :drill:\n:PROPERTIES:\n:ID: shared\n:DRILL_LAST_INTERVAL: 12.0\n:DRILL_TOTAL_REPEATS: 4\n:DRILL_REPEATS_SINCE_FAIL: 3\n:DRILL_FAILURE_COUNT: 1\n:DRILL_AVERAGE_QUALITY: 3.5\n:DRILL_EASE: 2.5\n:END:\n" + "* Card :drill:\n:PROPERTIES:\n:ID: shared\n:END:\n" + (clrhash org-drill-dest-id-table) + (org-drill--build-dest-id-table dest-buf) + (should (gethash "shared" org-drill-dest-id-table)) + (cl-letf (((symbol-function 'org-drill-progress-message) #'ignore)) + (org-drill--migrate-from-source src-buf dest-buf nil)) + ;; ID was consumed off the table. + (should-not (gethash "shared" org-drill-dest-id-table)) + ;; DEST got the scheduling data. + (with-current-buffer dest-buf + (goto-char (point-min)) + (should (equal "12.0" (org-entry-get (point) "DRILL_LAST_INTERVAL"))) + (should (equal "4" (org-entry-get (point) "DRILL_TOTAL_REPEATS")))))) + +(ert-deftest test-migrate-no-id-on-src-is-skipped () + "SRC entries without an ID don't touch DEST and don't drain the table." + (with-two-org-tempfiles + "* Card :drill:\n:PROPERTIES:\n:DRILL_LAST_INTERVAL: 5.0\n:END:\n" + "* Card :drill:\n:PROPERTIES:\n:ID: untouched\n:END:\n" + (clrhash org-drill-dest-id-table) + (org-drill--build-dest-id-table dest-buf) + (cl-letf (((symbol-function 'org-drill-progress-message) #'ignore)) + (org-drill--migrate-from-source src-buf dest-buf nil)) + (should (gethash "untouched" org-drill-dest-id-table)))) + +(ert-deftest test-migrate-non-drill-entry-skipped () + "Non-drill entries in SRC are skipped even when they have an ID." + (with-two-org-tempfiles + "* Note (no drill tag)\n:PROPERTIES:\n:ID: shared\n:END:\n" + "* Card :drill:\n:PROPERTIES:\n:ID: shared\n:END:\n" + (clrhash org-drill-dest-id-table) + (org-drill--build-dest-id-table dest-buf) + (cl-letf (((symbol-function 'org-drill-progress-message) #'ignore)) + (org-drill--migrate-from-source src-buf dest-buf nil)) + ;; Non-drill SRC didn't match, so the ID stays in the table. + (should (gethash "shared" org-drill-dest-id-table)))) + +(ert-deftest test-migrate-ignore-new-items-skips-unmatched-src () + "When ignore-new-items-p is t, unmatched SRC entries don't get copied." + (with-two-org-tempfiles + "* New card :drill:\n:PROPERTIES:\n:ID: src-only\n:END:\n" + "* Existing :drill:\n:PROPERTIES:\n:ID: dest-only\n:END:\n" + (clrhash org-drill-dest-id-table) + (org-drill--build-dest-id-table dest-buf) + (cl-letf (((symbol-function 'org-drill-progress-message) #'ignore) + ((symbol-function 'org-drill-copy-entry-to-other-buffer) + (lambda (&rest _) (error "should not be called")))) + (org-drill--migrate-from-source src-buf dest-buf t)) + ;; dest-only is still in the table because nothing migrated to consume it. + (should (gethash "dest-only" org-drill-dest-id-table)))) + +(provide 'test-org-drill-migrate-from-source) +;;; test-org-drill-migrate-from-source.el ends here -- cgit v1.2.3