See attached 2 patches. The "fixup" patch is just to see what changed from last time. Apply the other one.
Tests pass on emacs 28, 29, 30.2 with TZ=UTC. Tests pass on emacs 30.2 with TZ="Europe/Istanbul" and TZ="America/New_York". Tests pass on emacs 30.2 with TZ="Asia/Kathmandu" and TZ="Canada/Newfoundland" (except for `test-ox-icalendar/todo-repeater-until-utc') Ihor Radchenko <[email protected]> writes: > Morgan Smith <[email protected]> writes: > >> Tests do not pass on Emacs 28 because Ihor used `string-equal-ignore-case' in >> the latest commits. > > Fixed > https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=99ee1bcd6 > Thanks! > > Time zones are crazy indeed. There are many more bugs in this area. > There are also locales... > Oh dear. My todo list includes an item to run all the tests with the system time set near DST changes (using guix stuff again :P). I'm adding the locales to the todo list as well now. I'm sure I'll find some fun stuff there. >> Although personally I'm a fan of tests that show exactly what behavior the >> software does, even if it's wrong. If the behavior starts being wrong in a >> different way I'd like to know and the ":expected-result :failed" won't >> tell me. > > Because not every single aspect of Org behavior is documented, tests are > sometimes used as kind of reference about how the code *should* > behave. So, having a test for something implies that one or other > behavior (even if awkward) is intentional. In some cases, Org mode's > behavior is weird only on a first glance. However, that behavior may be > thoughtfully discussed on the mailing list in the past and then codified > in the tests. Having tests that simply test for knowingly erroneous > behavior will break such assumptions. > Ah I see. I like the tests as a way to rigorously document a bug but others might interpret that as rigorous documentation of org-mode itself. I would appreciate it if we could come up with a way to rigorously document a bug but I suppose if I just fix the bugs then it won't be needed. >> That being said, I did just realize that grepping the >> code for "todo" items is not very easy. Should I be using "XXX" comments >> instead? > > We use FIXME. It is more greppable in Org's context. > See https://orgmode.org/worg/org-maintenance.html#minor-major-releases > Noted! I should really read that document at some point :P >> +(ert-deftest test-org-habit/org-extend-today-until () >> + "Test habit graph with `org-extend-today-until' set." >> + :expected-result :failed >> ... >> + ;; TODO: actual result is >> + ;; (t "\nhabit * * \n") >> + (1 "\nhabit ** ! \n") > > Is this todo still relevant? > Yes that is the current behavior. I haven't fixed any bugs yet. Just trying to document them right now. >> + (dolist (test-time '(2009-10-15 >> + 2009-10-16 >> ... >> + (let ((expected-output-string >> + (cl-case test-time >> + (2009-10-15 >> + " * * * * * * * \n") >> + (2009-10-16 >> + "* * * * * * *! \n") >> ... > > I believe that you can simplify this by using an alist > '((2009-10-15 . "...") > (2009-10-16 . "...) ...) Done.
>From 3cef4b41fcdb6ff4d15e08061c923a8bc8b368af Mon Sep 17 00:00:00 2001 From: Morgan Smith <[email protected]> Date: Sat, 25 Oct 2025 11:29:01 -0400 Subject: [PATCH] fixup! 8527f2b6f2bc9b4e7afb2ee9f4cadff90c9c9ccb --- testing/lisp/test-org-habit.el | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/testing/lisp/test-org-habit.el b/testing/lisp/test-org-habit.el index 8add6322f..cfbd25dc3 100644 --- a/testing/lisp/test-org-habit.el +++ b/testing/lisp/test-org-habit.el @@ -142,7 +142,7 @@ test-org-habit/org-extend-today-until (string-equal (cl-case org-extend-today-until (0 "\nhabit * *! \n") - ;; TODO: actual result is + ;; Current behavior is: ;; (t "\nhabit * * \n") (1 "\nhabit ** ! \n") (2 "\nhabit * * ! \n")) @@ -192,7 +192,7 @@ test-org-habit/dst - State \"DONE\" from \"TODO\" [2009-10-25 Sun]" (should (string-equal - ;; TODO: we lost a day in the transition! actual result: + ;; we lost a day in the transition! actual result: ;; "\nhabit * *! \n" "\nhabit * *! \n" (progn @@ -290,24 +290,16 @@ test-org-habit/show-habits 2009-10-21 2009-10-22)) (let ((expected-output-string - (cl-case test-time - (2009-10-15 - " * * * * * * * \n") - (2009-10-16 - "* * * * * * *! \n") - (2009-10-17 - " * * * * * * ! \n") - (2009-10-18 - " * * * * * * ! \n") - (2009-10-19 - " * * * * * * ! \n") - (2009-10-20 - "* * * * * * ! \n") - (2009-10-21 - " * * * * * ! \n") - (2009-10-22 - " * * * * * ! \n") - (t (cl-assert nil t "Missing case for: %S!" (symbol-name test-time)))))) + (alist-get + test-time + '((2009-10-15 . " * * * * * * * \n") + (2009-10-16 . "* * * * * * *! \n") + (2009-10-17 . " * * * * * * ! \n") + (2009-10-18 . " * * * * * * ! \n") + (2009-10-19 . " * * * * * * ! \n") + (2009-10-20 . "* * * * * * ! \n") + (2009-10-21 . " * * * * * ! \n") + (2009-10-22 . " * * * * * ! \n"))))) (org-test-at-time (symbol-name test-time) (should (string-equal -- 2.51.0
>From f51bb047e87e3ad59da8c369571e3d35cc19b470 Mon Sep 17 00:00:00 2001 From: Morgan Smith <[email protected]> Date: Thu, 23 Jan 2025 15:35:50 -0500 Subject: [PATCH] testing/lisp/test-org-habit.el: Add org habit tests * testing/lisp/test-org-habit.el: New file full of tests. --- testing/lisp/test-org-habit.el | 421 +++++++++++++++++++++++++++++++++ 1 file changed, 421 insertions(+) create mode 100644 testing/lisp/test-org-habit.el diff --git a/testing/lisp/test-org-habit.el b/testing/lisp/test-org-habit.el new file mode 100644 index 000000000..beb6ee0d4 --- /dev/null +++ b/testing/lisp/test-org-habit.el @@ -0,0 +1,421 @@ +;;; test-org-habit.el --- Tests for org-habit.el -*- lexical-binding: t ; -*- + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Unit tests for Org Habits. + +;;; Code: + +(require 'org-test "../testing/org-test") +(require 'org-agenda) +(require 'org-habit) +(require 'test-org-agenda) + + +;; Tests + +(defvar org-test-habit-no-fluff-agenda + '(("f" "no fluff" agenda "" + ((org-agenda-overriding-header "") + (org-agenda-format-date "") + (org-agenda-span 'day) + (org-agenda-show-all-dates nil) + (org-agenda-todo-keyword-format "") + (org-agenda-prefix-format ""))))) + +(defun org-test-habit-agenda-string (repeater-type-string repeater-deadline?) + "Return an org habit test string. +REPEATER-TYPE-STRING is used as the repeater type (ex. \".+\"). +When REPEATER-DEADLINE? is non-nil, add a repeater deadline. +Order is determined by `org-log-states-order-reversed'." + (concat + "* TODO Shave +SCHEDULED: <2009-10-17 Sat " repeater-type-string "2d" + (if repeater-deadline? + "/4d" + "") + "> +:PROPERTIES: +:STYLE: habit +:LAST_REPEAT: [2009-10-19 Mon 00:36] +:END: +" + + (if org-log-states-order-reversed + "- State \"DONE\" from \"TODO\" [2009-10-15 Thu] +- State \"DONE\" from \"TODO\" [2009-10-12 Mon] +- CLOSING NOTE [2009-10-10 Sat] \\ + this style occurs when `org-log-done' is `note'. +- State \"DONE\" from \"TODO\" [2009-10-04 Sun] +- State \"DONE\" from \"TODO\" [2009-10-02 Fri] +- State \"DONE\" from \"TODO\" [2009-09-29 Tue] +- State \"DONE\" from \"TODO\" [2009-09-25 Fri] +- State \"DONE\" from \"TODO\" [2009-09-19 Sat] +- State \"DONE\" from \"TODO\" [2009-09-16 Wed] +- State \"DONE\" from \"TODO\" [2009-09-12 Sat]" + + "- State \"DONE\" from \"TODO\" [2009-09-12 Sat] +- State \"DONE\" from \"TODO\" [2009-09-16 Wed] +- State \"DONE\" from \"TODO\" [2009-09-19 Sat] +- State \"DONE\" from \"TODO\" [2009-09-25 Fri] +- State \"DONE\" from \"TODO\" [2009-09-29 Tue] +- State \"DONE\" from \"TODO\" [2009-10-02 Fri] +- State \"DONE\" from \"TODO\" [2009-10-04 Sun] +- CLOSING NOTE [2009-10-10 Sat] \\ + this style occurs when `org-log-done' is `note'. +- State \"DONE\" from \"TODO\" [2009-10-12 Mon] +- State \"DONE\" from \"TODO\" [2009-10-15 Thu]"))) + +(defmacro org-test-habit (&rest body) + "Run BODY multiple times for testing habits. +Add agenda from `org-test-habit-no-fluff-agenda' to +`org-agenda-custom-commands'. + +Use habit data from `org-test-habit-agenda-string' both with and without +a repeater deadline and the the log data reversed and not-reversed." + (declare (indent 0)) + `(let ((org-agenda-custom-commands + org-test-habit-no-fluff-agenda)) + (dolist (org-log-states-order-reversed '(t nil)) + (dolist (repeater-deadline? '(nil t)) + (dolist (repeater-type-string '(".+" "+" "++")) + (org-test-agenda-with-agenda + (org-test-habit-agenda-string repeater-type-string repeater-deadline?) + ,@body)))))) + +(ert-deftest test-org-habit/simple-habit () + "Test the agenda view for a simple habit." + ;; Avoid DST when TZ="Europe/Istanbul". See `test-org-habit/dst'. + (org-test-with-timezone "UTC0" + (org-test-at-time "2009-10-22" + (let ((org-agenda-custom-commands + org-test-habit-no-fluff-agenda) + (org-habit-graph-column 5)) + (org-test-agenda-with-agenda + "* TODO habit +SCHEDULED: <2009-10-21 Sat ++2d> +:PROPERTIES: +:STYLE: habit +:END: +- State \"DONE\" from \"TODO\" [2009-10-19 Sun] +- State \"DONE\" from \"TODO\" [2009-10-17 Sun]" + (should + (string-equal + "\nhabit * * ! \n" + (progn + (org-agenda nil "f") + (buffer-string))))))))) + +(ert-deftest test-org-habit/org-extend-today-until () + "Test habit graph with `org-extend-today-until' set." + :expected-result :failed + (org-test-at-time "2009-10-20" + (let ((org-agenda-custom-commands + org-test-habit-no-fluff-agenda) + (org-habit-preceding-days 5) + (org-habit-following-days 5) + (org-habit-graph-column 5) + (org-habit-show-all-today t)) + (dolist (org-extend-today-until '(0 1 2)) + (org-test-agenda-with-agenda + "* TODO habit +SCHEDULED: <2009-10-20 Sat ++1d> +:PROPERTIES: +:STYLE: habit +:END: +- State \"DONE\" from \"TODO\" [2009-10-19 Sun 00:20] +- State \"DONE\" from \"TODO\" [2009-10-17 Sun 01:20]" + (should + (string-equal + (cl-case org-extend-today-until + (0 "\nhabit * *! \n") + ;; Current behavior is: + ;; (t "\nhabit * * \n") + (1 "\nhabit ** ! \n") + (2 "\nhabit * * ! \n")) + (progn + (org-agenda nil "f") + (buffer-string))))))))) + +(ert-deftest test-org-habit/dst () + "Test the habit graph traversing a daylight savings time transition." + :expected-result :failed + (org-test-with-timezone "America/New_York" + ;; DST transition (spring forward) [2009-03-08 01:59] -> [2009-03-08 03:00] + (org-test-at-time "2009-03-05" + (let ((org-agenda-custom-commands + org-test-habit-no-fluff-agenda) + (org-habit-preceding-days 5) + (org-habit-following-days 5) + (org-habit-graph-column 5)) + (org-test-agenda-with-agenda + "* TODO habit +SCHEDULED: <2009-03-05 Sat ++1d> +:PROPERTIES: +:STYLE: habit +:END: +- State \"DONE\" from \"TODO\" [2009-03-04 Wed] +- State \"DONE\" from \"TODO\" [2009-03-02 Mon]" + (should + (string-equal + "\nhabit * *! \n" + (progn + (org-agenda nil "f") + (buffer-string))))))) + ;; DST transition (fall back) [2009-11-01 01:59] -> [2009-11-01 01:00] + (org-test-at-time "2009-10-28" + (let ((org-agenda-custom-commands + org-test-habit-no-fluff-agenda) + (org-habit-preceding-days 5) + (org-habit-following-days 5) + (org-habit-graph-column 5)) + (org-test-agenda-with-agenda + "* TODO habit +SCHEDULED: <2009-10-28 Sat ++1d> +:PROPERTIES: +:STYLE: habit +:END: +- State \"DONE\" from \"TODO\" [2009-10-27 Sun] +- State \"DONE\" from \"TODO\" [2009-10-25 Sun]" + (should + (string-equal + ;; we lost a day in the transition! actual result: + ;; "\nhabit * *! \n" + "\nhabit * *! \n" + (progn + (org-agenda nil "f") + (buffer-string))))))))) + +(ert-deftest test-org-habit/habit () + "Test the agenda view for a habit." + (org-test-at-time "2009-10-17" + (org-test-habit + (should + (string-equal + "\nShave * * * * * * ! \n" + (progn + (org-agenda nil "f") + (buffer-string))))))) + +(ert-deftest test-org-habit/graph-column () + "Test how modifiying `org-habit-graph-column' affects habits in the agenda." + (org-test-at-time "2009-10-17" + (org-test-habit + (dolist (org-habit-graph-column '(0 1 2 3 10 20 40 100)) + (should + (string-equal + (cl-case org-habit-graph-column + (0 "\n * * * * * * ! \n") + (1 "\nS * * * * * * ! \n") + (2 "\nSh * * * * * * ! \n") + (3 "\nSha * * * * * * ! \n") + ((10 20 40 100) (concat "\nShave" + (make-string (- org-habit-graph-column 2) 32) + "* * * * * * ! \n")) + (t (cl-assert nil nil "Missing case!"))) + (progn + (org-agenda nil "f") + (buffer-string)))))))) + +(ert-deftest test-org-habit/preceding-days () + "Test how modifiying `org-habit-preceding-days' affects habits in the agenda." + (org-test-at-time "2009-10-17" + (org-test-habit + (dolist (org-habit-preceding-days '(0 1 2 3 10 20 40 100)) + (should + (string-equal + (cl-case org-habit-preceding-days + (0 " ! \n") + (1 " ! \n") + (2 " * ! \n") + (3 " * ! \n") + (10 " * * * ! \n") + (20 " * * * * * * ! \n") + ((40 100) (concat (make-string (- org-habit-preceding-days 34) 32) + "* * * * * * * * * * ! \n")) + (t (cl-assert nil nil "Missing case!"))) + (progn + (org-agenda nil "f") + (buffer-substring (+ 1 org-habit-graph-column) (point-max))))))))) + +(ert-deftest test-org-habit/following-days () + "Test how modifiying `org-habit-following-days' affects habits in the agenda." + ;; Avoid DST when TZ="America/New_York". See `test-org-habit/dst'. + (org-test-with-timezone "UTC0" + (org-test-at-time "2009-10-17" + (org-test-habit + (dolist (org-habit-following-days '(0 1 2 3 10 20 40 100)) + (should + (string-equal + (cl-case org-habit-following-days + (0 " * * * * * * \n") + ((1 2 3 10 20 40 100) + (concat " * * * * * * !" + (make-string org-habit-following-days 32) + "\n")) + (t (cl-assert nil nil "Missing case!"))) + (progn + (org-agenda nil "f") + (buffer-substring (+ 1 org-habit-graph-column) (point-max)))))))))) + +(ert-deftest test-org-habit/show-habits () + "Test displaying habits in the agenda at various points in time. +Also test modifying the variables `org-habit-show-habits', +`org-habit-show-habits-only-for-today', and `org-habit-show-all-today'." + ;; Avoid DST when TZ="Europe/Istanbul". See `test-org-habit/dst'. + (org-test-with-timezone "UTC0" + (org-test-habit + (dolist (org-habit-show-habits '(nil t)) + (dolist (org-habit-show-habits-only-for-today '(nil t)) + (dolist (org-habit-show-all-today '(nil t)) + (dolist (test-time '(2009-10-15 + 2009-10-16 + 2009-10-17 + 2009-10-18 + 2009-10-19 + 2009-10-20 + 2009-10-21 + 2009-10-22)) + (let ((expected-output-string + (alist-get + test-time + '((2009-10-15 . " * * * * * * * \n") + (2009-10-16 . "* * * * * * *! \n") + (2009-10-17 . " * * * * * * ! \n") + (2009-10-18 . " * * * * * * ! \n") + (2009-10-19 . " * * * * * * ! \n") + (2009-10-20 . "* * * * * * ! \n") + (2009-10-21 . " * * * * * ! \n") + (2009-10-22 . " * * * * * ! \n"))))) + (org-test-at-time (symbol-name test-time) + (should + (string-equal + (if org-habit-show-habits + (cl-case test-time + ((2009-10-15 2009-10-16) + (if org-habit-show-all-today + expected-output-string + "")) + ((2009-10-17 2009-10-18 2009-10-19 2009-10-20 2009-10-21 2009-10-22) + expected-output-string) + (t (cl-assert nil t "Missing case for: %S!" (symbol-name test-time)))) + "") + (progn + (org-agenda nil "f") + (let ((result (buffer-string))) + (if (string-empty-p result) + result + (substring result (+ 1 org-habit-graph-column)))))))))))))))) + +(ert-deftest test-org-habit/toggle-display-in-agenda () + "Test the agenda view for a habit." + (let ((org-agenda-custom-commands + '(("f" "no fluff" agenda "" + ;; This differs from `org-test-habit-no-fluff-agenda' by + ;; adding this header. Without this we have cases where + ;; the agenda buffer is completly empty and that causes + ;; funny things to happen + ((org-agenda-overriding-header "h") + (org-agenda-format-date "") + (org-agenda-span 'day) + (org-agenda-show-all-dates nil) + (org-agenda-todo-keyword-format "") + (org-agenda-prefix-format ""))))) + (org-habit-graph-column 7) + (org-habit-following-days 1) + (org-habit-preceding-days 5)) + ;; (test-time . expected-string) + (dolist (test-data '(("2009-10-15" . "h\n\nShave * * * \n") + ("2009-10-17" . "h\n\nShave * * ! \n"))) + (org-test-at-time (car test-data) + (org-test-agenda-with-agenda + (org-test-habit-agenda-string "++" nil) + (org-agenda nil "f") + (should + (string-equal + (if (string-equal (car test-data) "2009-10-17") + (cdr test-data) + "h\n") + (buffer-string))) + (org-habit-toggle-display-in-agenda nil) + (should + (string-equal + "h\n" + (buffer-string))) + (org-habit-toggle-display-in-agenda nil) + (should + (string-equal + (if (string-equal (car test-data) "2009-10-17") + (cdr test-data) + "h\n") + (buffer-string))) + (org-habit-toggle-display-in-agenda t) + (should + (string-equal + (cdr test-data) + (buffer-string)))))))) + +;;; Bad habits + +(ert-deftest test-org-habit/bad-habit-no-repeater () + "Test a habit without a repeater." + (org-test-agenda-with-agenda + "* TODO no repeater +SCHEDULED: <2009-10-17 Sat> +:PROPERTIES: +:STYLE: habit +:END:" + (should-error + (org-agenda nil "a")))) + +(ert-deftest test-org-habit/bad-habit-short-repeater () + "Test a habit with a period of less then 1 day." + (org-test-agenda-with-agenda + "* TODO repeat period less then 1 day +SCHEDULED: <2009-10-17 Sat +0d> +:PROPERTIES: +:STYLE: habit +:END:" + (should-error + (org-agenda nil "a")))) + +(ert-deftest test-org-habit/bad-habit-no-scheduled () + "Test a habit that is not scheduled." + (org-test-agenda-with-agenda + "* TODO no scheduled <2009-10-17 Sat +1d> +:PROPERTIES: +:STYLE: habit +:END:" + (should-error + (org-agenda nil "a")))) + +(ert-deftest test-org-habit/bad-habit-deadline-less-scheduled () + "Test a habit where the deadline is less then or equal to the scheduled." + (dolist (deadline '("1d" "2d")) + (org-test-agenda-with-agenda + (concat + "* TODO deadline < or = to scheduled +SCHEDULED: <2009-10-17 Sat +2d/" deadline "> +:PROPERTIES: +:STYLE: habit +:END:") + (should-error + (org-agenda nil "a"))))) + + +(provide 'test-org-habit) + +;;; test-org-habit.el ends here base-commit: ea38f257d65d1dad02fa58da06b74682e40db0f3 -- 2.51.0
