aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2025-11-13 11:45:30 -0600
committerCraig Jennings <c@cjennings.net>2025-11-13 11:45:30 -0600
commit1618ff6b2cfe463ee225f11c2772712f8243c6d3 (patch)
tree06b72938645c722e30064e5fe00c6001e547c23b
parentcf182d001f66164232b8735bad43eb07121109c6 (diff)
downloadorg-drill-1618ff6b2cfe463ee225f11c2772712f8243c6d3.tar.gz
org-drill-1618ff6b2cfe463ee225f11c2772712f8243c6d3.zip
test: Add Phase 2 card type tests
- Add unit tests for simple card type (11 tests) - Add unit tests for twosided card type (11 tests) - Add unit tests for hide1cloze card type (4 tests) Total: 91 tests (80 unit + 11 integration), all passing
-rw-r--r--tests/test-card-type-hide1cloze.el47
-rw-r--r--tests/test-card-type-simple.el213
-rw-r--r--tests/test-card-type-twosided.el218
3 files changed, 478 insertions, 0 deletions
diff --git a/tests/test-card-type-hide1cloze.el b/tests/test-card-type-hide1cloze.el
new file mode 100644
index 0000000..d203c4f
--- /dev/null
+++ b/tests/test-card-type-hide1cloze.el
@@ -0,0 +1,47 @@
+;;; test-card-type-hide1cloze.el --- Tests for hide1cloze card type
+
+;;; Commentary:
+;; Tests for the hide1cloze card type in org-drill.
+
+;;; Code:
+
+(require 'ert)
+(require 'assess)
+(require 'org-drill)
+
+;;; Normal Cases - Card Recognition
+
+(ert-deftest test-card-type-hide1cloze-normal-card-type-property ()
+ "Test that hide1cloze cards have correct DRILL_CARD_TYPE property."
+ (with-temp-buffer
+ (org-mode)
+ (insert "* Basic Cloze Card :drill:\n:PROPERTIES:\n:DRILL_CARD_TYPE: hide1cloze\n:END:\n\nThe capital of France is Paris.\n")
+ (goto-char (point-min))
+ (should (org-drill-entry-p))
+ (should (equal "hide1cloze" (org-entry-get (point) "DRILL_CARD_TYPE")))))
+
+(ert-deftest test-card-type-hide1cloze-normal-has-presentation-function ()
+ "Test that hide1cloze card type has correct presentation function."
+ (let ((entry (assoc "hide1cloze" org-drill-card-type-alist)))
+ (should entry)
+ (should (eq (cadr entry) 'org-drill-present-multicloze-hide1))))
+
+(ert-deftest test-card-type-hide1cloze-normal-multicloze-alias ()
+ "Test that multicloze is an alias for hide1cloze."
+ (let ((hide1-entry (assoc "hide1cloze" org-drill-card-type-alist))
+ (multi-entry (assoc "multicloze" org-drill-card-type-alist)))
+ (should hide1-entry)
+ (should multi-entry)
+ (should (eq (cadr hide1-entry) (cadr multi-entry)))
+ (should (eq (cadr multi-entry) 'org-drill-present-multicloze-hide1))))
+
+(ert-deftest test-card-type-hide1cloze-normal-different-from-show1cloze ()
+ "Test that hide1cloze and show1cloze use different functions."
+ (let ((hide1-fn (cadr (assoc "hide1cloze" org-drill-card-type-alist)))
+ (show1-fn (cadr (assoc "show1cloze" org-drill-card-type-alist))))
+ (should-not (eq hide1-fn show1-fn))
+ (should (eq hide1-fn 'org-drill-present-multicloze-hide1))
+ (should (eq show1-fn 'org-drill-present-multicloze-show1))))
+
+(provide 'test-card-type-hide1cloze)
+;;; test-card-type-hide1cloze.el ends here
diff --git a/tests/test-card-type-simple.el b/tests/test-card-type-simple.el
new file mode 100644
index 0000000..5770883
--- /dev/null
+++ b/tests/test-card-type-simple.el
@@ -0,0 +1,213 @@
+;;; test-card-type-simple.el --- Tests for simple card type
+
+;;; Commentary:
+;; Tests for the simple card type in org-drill.
+;;
+;; Simple cards are the default card type. They show the question
+;; (entry body) and hide subheadings. The answer is revealed in
+;; subheadings or can be specified in DRILL_ANSWER property.
+;;
+;; Card type: nil or "simple"
+;; Presentation function: org-drill-present-simple-card
+
+;;; Code:
+
+(require 'ert)
+(require 'assess)
+(require 'org-drill)
+
+;;; Test Data
+
+(defconst test-card-type-simple-basic-card
+ "* Simple Card :drill:
+
+Question: What is the capital of France?
+
+** Answer
+
+Paris
+"
+ "Basic simple card with question and answer in subheading.")
+
+(defconst test-card-type-simple-with-property
+ "* Simple Card with Property :drill:
+:PROPERTIES:
+:DRILL_CARD_TYPE: simple
+:DRILL_ANSWER: The answer is 42
+:END:
+
+Question: What is the meaning of life?
+"
+ "Simple card with DRILL_ANSWER property.")
+
+(defconst test-card-type-simple-multiple-subheadings
+ "* Card with Multiple Subheadings :drill:
+
+Main question content here.
+
+** First Answer
+
+First part of answer.
+
+** Second Answer
+
+Second part of answer.
+
+** Notes
+
+Additional notes that should be hidden.
+"
+ "Simple card with multiple subheadings.")
+
+(defconst test-card-type-simple-no-answer
+ "* Card without Answer :drill:
+
+Just a question, no answer subheading.
+"
+ "Simple card with no answer subheading.")
+
+;;; Helper Functions
+
+(defun test-card-type-simple--with-card (content callback)
+ "Execute CALLBACK in temp buffer with drill card CONTENT."
+ (with-temp-buffer
+ (org-mode)
+ (insert content)
+ (goto-char (point-min))
+ (funcall callback)))
+
+(defun test-card-type-simple--card-has-type-p (card-type)
+ "Check if current entry has CARD-TYPE."
+ (let ((entry-type (org-entry-get (point) "DRILL_CARD_TYPE" t)))
+ (or (and (null card-type) (null entry-type))
+ (equal card-type entry-type))))
+
+;;; Normal Cases - Card Recognition
+
+(ert-deftest test-card-type-simple-normal-default-card-type ()
+ "Test that cards without DRILL_CARD_TYPE are treated as simple cards."
+ (test-card-type-simple--with-card
+ test-card-type-simple-basic-card
+ (lambda ()
+ (should (org-drill-entry-p))
+ (should (null (org-entry-get (point) "DRILL_CARD_TYPE"))))))
+
+(ert-deftest test-card-type-simple-normal-explicit-simple-type ()
+ "Test card with explicit 'simple' type in DRILL_CARD_TYPE property."
+ (test-card-type-simple--with-card
+ test-card-type-simple-with-property
+ (lambda ()
+ (should (org-drill-entry-p))
+ (should (equal "simple" (org-entry-get (point) "DRILL_CARD_TYPE"))))))
+
+(ert-deftest test-card-type-simple-normal-has-presentation-function ()
+ "Test that simple card type has correct presentation function registered."
+ (let ((simple-entry (assoc "simple" org-drill-card-type-alist))
+ (nil-entry (assoc nil org-drill-card-type-alist)))
+ ;; Both nil and "simple" should map to org-drill-present-simple-card
+ (should simple-entry)
+ (should nil-entry)
+ (should (eq (cadr simple-entry) 'org-drill-present-simple-card))
+ (should (eq (cadr nil-entry) 'org-drill-present-simple-card))))
+
+;;; Normal Cases - Card Structure
+
+(ert-deftest test-card-type-simple-normal-single-answer-subheading ()
+ "Test simple card with single answer subheading."
+ (test-card-type-simple--with-card
+ test-card-type-simple-basic-card
+ (lambda ()
+ (should (org-drill-entry-p))
+ ;; Should have at least one subheading
+ (save-excursion
+ (org-back-to-heading)
+ (let ((has-subheading nil))
+ (org-map-entries
+ (lambda () (setq has-subheading t))
+ nil 'tree)
+ (should has-subheading))))))
+
+(ert-deftest test-card-type-simple-normal-multiple-answer-subheadings ()
+ "Test simple card with multiple subheadings."
+ (test-card-type-simple--with-card
+ test-card-type-simple-multiple-subheadings
+ (lambda ()
+ (should (org-drill-entry-p))
+ ;; Count subheadings
+ (save-excursion
+ (org-back-to-heading)
+ (let ((subheading-count 0))
+ (org-map-entries
+ (lambda ()
+ (when (> (org-current-level) 1)
+ (cl-incf subheading-count)))
+ nil 'tree)
+ (should (>= subheading-count 3)))))))
+
+;;; Normal Cases - DRILL_ANSWER Property
+
+(ert-deftest test-card-type-simple-normal-drill-answer-property ()
+ "Test simple card with DRILL_ANSWER property."
+ (test-card-type-simple--with-card
+ test-card-type-simple-with-property
+ (lambda ()
+ (should (org-drill-entry-p))
+ (let ((answer (org-entry-get (point) "DRILL_ANSWER")))
+ (should answer)
+ (should (string-match-p "42" answer))))))
+
+;;; Boundary Cases
+
+(ert-deftest test-card-type-simple-boundary-no-answer-subheading ()
+ "Test simple card without answer subheading.
+Card should still be valid, answer would come from DRILL_ANSWER property."
+ (test-card-type-simple--with-card
+ test-card-type-simple-no-answer
+ (lambda ()
+ (should (org-drill-entry-p))
+ ;; No subheadings
+ (save-excursion
+ (org-back-to-heading)
+ (let ((has-subheading nil))
+ (org-map-entries
+ (lambda ()
+ (when (> (org-current-level) 1)
+ (setq has-subheading t)))
+ nil 'tree)
+ (should-not has-subheading))))))
+
+(ert-deftest test-card-type-simple-boundary-empty-question ()
+ "Test simple card with empty question body."
+ (let ((content "* Empty Question :drill:\n\n** Answer\n\nSome answer\n"))
+ (test-card-type-simple--with-card
+ content
+ (lambda ()
+ (should (org-drill-entry-p))))))
+
+(ert-deftest test-card-type-simple-boundary-very-long-question ()
+ "Test simple card with very long question text."
+ (let* ((long-text (make-string 5000 ?x))
+ (content (format "* Long Question :drill:\n\n%s\n\n** Answer\n\nYes\n" long-text)))
+ (test-card-type-simple--with-card
+ content
+ (lambda ()
+ (should (org-drill-entry-p))))))
+
+;;; Card Type Lookup
+
+(ert-deftest test-card-type-simple-normal-card-type-lookup ()
+ "Test that card type lookup works for simple cards."
+ (let ((presentation-fn (cadr (assoc nil org-drill-card-type-alist))))
+ (should (eq presentation-fn 'org-drill-present-simple-card)))
+ (let ((presentation-fn (cadr (assoc "simple" org-drill-card-type-alist))))
+ (should (eq presentation-fn 'org-drill-present-simple-card))))
+
+(ert-deftest test-card-type-simple-normal-default-vs-explicit ()
+ "Test that default (nil) and explicit 'simple' type behave identically."
+ (let ((default-fn (cadr (assoc nil org-drill-card-type-alist)))
+ (explicit-fn (cadr (assoc "simple" org-drill-card-type-alist))))
+ (should (eq default-fn explicit-fn))
+ (should (eq default-fn 'org-drill-present-simple-card))))
+
+(provide 'test-card-type-simple)
+;;; test-card-type-simple.el ends here
diff --git a/tests/test-card-type-twosided.el b/tests/test-card-type-twosided.el
new file mode 100644
index 0000000..cda508f
--- /dev/null
+++ b/tests/test-card-type-twosided.el
@@ -0,0 +1,218 @@
+;;; test-card-type-twosided.el --- Tests for two-sided card type
+
+;;; Commentary:
+;; Tests for the two-sided card type in org-drill.
+;;
+;; Two-sided cards have two subheadings representing two "sides" of a card.
+;; When presented, one of the first two subheadings is randomly chosen
+;; and shown as the question, while the other serves as the answer.
+;;
+;; Card type: "twosided"
+;; Presentation function: org-drill-present-two-sided-card
+
+;;; Code:
+
+(require 'ert)
+(require 'assess)
+(require 'org-drill)
+
+;;; Test Data
+
+(defconst test-card-type-twosided-basic-card
+ "* Two-Sided Card :drill:
+:PROPERTIES:
+:DRILL_CARD_TYPE: twosided
+:END:
+
+** Side 1
+
+English: Apple
+
+** Side 2
+
+Spanish: Manzana
+"
+ "Basic two-sided card for vocabulary.")
+
+(defconst test-card-type-twosided-with-extra-sides
+ "* Card with Extra Sides :drill:
+:PROPERTIES:
+:DRILL_CARD_TYPE: twosided
+:END:
+
+** Side A
+
+First side content.
+
+** Side B
+
+Second side content.
+
+** Side C
+
+Third side (should not be shown in two-sided mode).
+
+** Side D
+
+Fourth side (should not be shown in two-sided mode).
+"
+ "Two-sided card with more than 2 subheadings.")
+
+(defconst test-card-type-twosided-single-side
+ "* Card with Single Side :drill:
+:PROPERTIES:
+:DRILL_CARD_TYPE: twosided
+:END:
+
+** Only Side
+
+Only one subheading present.
+"
+ "Two-sided card with only one subheading.")
+
+(defconst test-card-type-twosided-empty-sides
+ "* Card with Empty Sides :drill:
+:PROPERTIES:
+:DRILL_CARD_TYPE: twosided
+:END:
+
+** Side 1
+
+** Side 2
+
+"
+ "Two-sided card with empty side content.")
+
+;;; Helper Functions
+
+(defun test-card-type-twosided--with-card (content callback)
+ "Execute CALLBACK in temp buffer with drill card CONTENT."
+ (with-temp-buffer
+ (org-mode)
+ (insert content)
+ (goto-char (point-min))
+ (funcall callback)))
+
+(defun test-card-type-twosided--count-subheadings ()
+ "Count number of level-2 subheadings under current entry."
+ (save-excursion
+ (org-back-to-heading)
+ (let ((count 0)
+ (end (save-excursion (org-end-of-subtree t t))))
+ (while (and (re-search-forward "^\\*\\* " end t))
+ (cl-incf count))
+ count)))
+
+;;; Normal Cases - Card Recognition
+
+(ert-deftest test-card-type-twosided-normal-card-type-property ()
+ "Test that two-sided cards have correct DRILL_CARD_TYPE property."
+ (test-card-type-twosided--with-card
+ test-card-type-twosided-basic-card
+ (lambda ()
+ (should (org-drill-entry-p))
+ (should (equal "twosided" (org-entry-get (point) "DRILL_CARD_TYPE"))))))
+
+(ert-deftest test-card-type-twosided-normal-has-presentation-function ()
+ "Test that twosided card type has correct presentation function registered."
+ (let ((entry (assoc "twosided" org-drill-card-type-alist)))
+ (should entry)
+ (should (eq (cadr entry) 'org-drill-present-two-sided-card))))
+
+(ert-deftest test-card-type-twosided-normal-drill-empty-p-flag ()
+ "Test that twosided card type has drill-empty-p flag set.
+This allows two-sided cards to be presented even if main body is empty."
+ (let ((entry (assoc "twosided" org-drill-card-type-alist)))
+ (should entry)
+ ;; Fourth element is drill-empty-p flag
+ (should (nth 3 entry))))
+
+;;; Normal Cases - Card Structure
+
+(ert-deftest test-card-type-twosided-normal-two-subheadings ()
+ "Test two-sided card with exactly two subheadings."
+ (test-card-type-twosided--with-card
+ test-card-type-twosided-basic-card
+ (lambda ()
+ (should (org-drill-entry-p))
+ (should (= 2 (test-card-type-twosided--count-subheadings))))))
+
+(ert-deftest test-card-type-twosided-normal-more-than-two-subheadings ()
+ "Test two-sided card with more than two subheadings.
+Only first two should be used for presentation."
+ (test-card-type-twosided--with-card
+ test-card-type-twosided-with-extra-sides
+ (lambda ()
+ (should (org-drill-entry-p))
+ (should (> (test-card-type-twosided--count-subheadings) 2)))))
+
+;;; Boundary Cases
+
+(ert-deftest test-card-type-twosided-boundary-single-subheading ()
+ "Test two-sided card with only one subheading.
+Should still be a valid card, though not ideal."
+ (test-card-type-twosided--with-card
+ test-card-type-twosided-single-side
+ (lambda ()
+ (should (org-drill-entry-p))
+ (should (= 1 (test-card-type-twosided--count-subheadings))))))
+
+(ert-deftest test-card-type-twosided-boundary-empty-side-content ()
+ "Test two-sided card where sides have no content."
+ (test-card-type-twosided--with-card
+ test-card-type-twosided-empty-sides
+ (lambda ()
+ (should (org-drill-entry-p))
+ (should (= 2 (test-card-type-twosided--count-subheadings))))))
+
+(ert-deftest test-card-type-twosided-boundary-no-main-body ()
+ "Test two-sided card with no main body content.
+This is valid because twosided has drill-empty-p flag."
+ (let ((content "* No Body :drill:\n:PROPERTIES:\n:DRILL_CARD_TYPE: twosided\n:END:\n\n** A\n\nContent A\n\n** B\n\nContent B\n"))
+ (test-card-type-twosided--with-card
+ content
+ (lambda ()
+ (should (org-drill-entry-p))
+ (should (equal "twosided" (org-entry-get (point) "DRILL_CARD_TYPE")))))))
+
+;;; Card Type Semantics
+
+(ert-deftest test-card-type-twosided-normal-vocabulary-use-case ()
+ "Test typical vocabulary card use case.
+Two sides for word translation pairs."
+ (test-card-type-twosided--with-card
+ test-card-type-twosided-basic-card
+ (lambda ()
+ (should (org-drill-entry-p))
+ ;; Verify both sides exist
+ (let ((sides nil))
+ (save-excursion
+ (org-back-to-heading)
+ (while (re-search-forward "^\\*\\* " nil t)
+ (let ((heading-text (buffer-substring
+ (point)
+ (line-end-position))))
+ (push heading-text sides))))
+ (should (= 2 (length sides)))
+ (should (member "Side 1" sides))
+ (should (member "Side 2" sides))))))
+
+(ert-deftest test-card-type-twosided-normal-concept-definition-use-case ()
+ "Test concept/definition card use case."
+ (let ((content "* Concept Card :drill:\n:PROPERTIES:\n:DRILL_CARD_TYPE: twosided\n:END:\n\n** Concept\n\nPolymorphism\n\n** Definition\n\nAbility of different objects to respond to same message in different ways.\n"))
+ (test-card-type-twosided--with-card
+ content
+ (lambda ()
+ (should (org-drill-entry-p))
+ (should (= 2 (test-card-type-twosided--count-subheadings)))))))
+
+(ert-deftest test-card-type-twosided-normal-different-from-simple ()
+ "Test that twosided is registered differently from simple."
+ (let ((twosided-fn (cadr (assoc "twosided" org-drill-card-type-alist)))
+ (simple-fn (cadr (assoc "simple" org-drill-card-type-alist))))
+ (should-not (eq twosided-fn simple-fn))
+ (should (eq twosided-fn 'org-drill-present-two-sided-card))
+ (should (eq simple-fn 'org-drill-present-simple-card))))
+
+(provide 'test-card-type-twosided)
+;;; test-card-type-twosided.el ends here