org-mode/testing/lisp/test-org-agenda.el

873 lines
34 KiB
EmacsLisp
Raw Normal View History

;;; test-org-agenda.el --- Tests for org-agenda.el -*- lexical-binding: t ; -*-
2019-01-01 10:50:56 +00:00
;; Copyright (C) 2017, 2019 Marco Wahl
;; Author: Marco Wahl <marcowahlsoft@gmail.com>
;; 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 Agenda.
;;; Code:
* lisp/org-agenda.el: Use lexical-binding - Removed the global (defvar date) and (defvar entry) so as not to conflict with function arguments of that name. Instead I added such `defvar`s in the body of each of the functions where it seemed needed. - I added some FIXMEs for some issues I found along the way. - Added an `org-dlet` macro, just like I had done for `calendar-dlet`, but I also use `defvar` "manually" at some places, when splitting an existing `let` into a mix of `let`s and `dlet`s seemed too much trouble. - Removed uses of `org-let and `org-let2` not only because I consider them offensive to my sense of aesthetics but also because they're basically incompatible with lexical scoping. I replaced them with uses of `cl-progv` which are a bit more verbose. Maybe we should define some `org-progv` macro on top of `cl-progv` to make the code less verbose, but I didn't do that because I like the fact that the current code makes uses of `eval` a bit more obvious (since these behave differently with lexical scoping than with lexical binding, it seemed worthwhile). - Removed the use of `eval` in `org-store-agenda-views` which was only placed there in order to use a macro before it's defined (it would have been simpler/cleaner to just move that functions *after* the macro, but with the new code the problem doesn't occur any more anyway). - Replaced a few `(lambda...) with actual closures. Detailed changes follow: (date, entry): Don't declare as being globally dynbound. (org-agenda-format-date-aligned): Remove unused var `weekyear`. (org-agenda-mode): `run-mode-hooks` is always available nowadays. (org-agenda-undo): Remove unused var `last-undo-buffer`. (org-agenda): Rename arg to `keys` and then dyn-bind it as `org-keys`. Remove unused vars `buf` and `key`. (org-agenda): Use `pcase` and `cl-progv` instead of `org-let`. (org-let, org-let2): Mark as obsolete. (org-agenda-run-series): Use `cl-progv` instead of `org-let` and `org-let2`. (org-agenda-run-series): New function. (org--batch-agenda): New function extracted from `org-batch-agenda`. (org-batch-agenda): Use it. (org--batch-agenda-csv): New function extracted from `org-batch-agenda-csv`. (org-batch-agenda-csv): Use it. (org--batch-store-agenda-views): New function, extracted from `org-batch-store-agenda-views`. (org-store-agenda-views, org-batch-store-agenda-views): Use it. (org--batch-store-agenda-views): Use `cl-progv` instead of `org-eval-in-environment`. (org-agenda-write): Use `cl-progv` instead of `org-let`. Use `with-current-buffer`. (org-agenda-filter-any): Use `cl-some` instead of `eval`. (org-agenda-list): Remove unused var `e`. (org-search-view): η-reduce. (crm-separator): Declare var. (org-agenda-skip-if): Remove unused var `beg`. (org-agenda-list-stuck-projects): Use a closure rather than `(lambda..). (diary-modify-entry-list-string-function, diary-file-name-prefix) (diary-display-function): Declare vars. (org-diary): Declare `date` and `entry` as dynbound. (org-agenda-get-day-entries): Use `org-dlet`. (org-agenda-get-timestamps, org-agenda-get-progress) (org-agenda-get-deadlines, org-agenda-get-scheduled, org-agenda-get-blocks): Declare `date` as dynbound. (org-agenda-get-sexps, org-class): Declare `date` and `entry` as dynbound. (org-agenda-format-item): Declare the vars mentioned in `org-compile-prefix-format` as dyn-bound. Also binding `extra`, suggested by Kyle Meyer <kyle@kyleam.com>. (org-compile-prefix-format): Remove unused var `e`. Use `member` rather than or+equal. (org-set-sorting-strategy): Minor simplification. (org-entries-lessp): Use `org-dlet`. (org-agenda-redo): Declare var `org-agenda-tag-filter-while-redo`. (org-agenda-redo): Use `cl-progv` rather than `org-let`. (org-agenda-filter): Remove unused var `rpl-fn`. Use `org-pushnew-to-end` to replace `add-to-list` on lexical var. (org-agenda-filter-by-tag): Remove unused var `n`. (org-agenda-filter-apply): Use `org-dlet`. (org-agenda-compute-starting-span): Remove unused var `dg`. (org-agenda-forward-block): Remove unused var `pos`. (org-archive-from-agenda): Declare var. (org-agenda-refile): Remove unused var `pos`. (org-agenda-headline-snapshot-before-repeat): Declare var. (org-agenda-todo): Remove redundant use of `bound-and-true-p`. (org-agenda-add-note): Remove unused var `hdmarker` and unused `arg`. (org-agenda-change-all-lines): Remove unused var `pl`. (org-agenda-priority): Remove unused var `marker`. (org-agenda-set-effort): Remove unused var `newhead`. (org-agenda-schedule): Remove unused var `type`. (org-agenda-clock-cancel): Remove unused `arg`. (org-agenda-execute-calendar-command): Use `org-dlet`. (org-agenda-bulk-action): Use closures instead of `(lambda ...). (org-agenda-show-the-flagging-note): Remove unused vars `heading` and `newhead`. (org-agenda-remove-flag): Avoid `setq`. * testing/org-test.el (org--compile-when): New macro. (org-test-jump): Use it so compilation doesn't fail or generate broken code when `jump` is not available. * testing/lisp/test-org-src.el: * testing/lisp/test-org-attach.el: * testing/lisp/test-org-agenda.el: * testing/lisp/test-ob-java.el: Pass explicit filename to `require` so as not to rely on ".../testing" being in `load-path` during compilation. * lisp/org-num.el: Require` org`. * lisp/org-macs.el (org-eval-in-environment): Declare obsolete. (org-dlet, org-pushnew-to-end): New macros. * doc/Makefile (org.texi, orgguide.texi, %_letter.tex): Simplify quoting. * contrib/lisp/ob-sclang.el: Don't crash compilation when `sclang` is not available. * contrib/lisp/ob-clojure-literate.el: Don't crash compilation when `cider` is not available. * contrib/lisp/ob-arduino.el: Don't crash compilation when `arduino-mode` is not available. * .gitignore: Add files generated during `make packages/org`.
2021-02-23 20:47:29 +00:00
(require 'org-test "../testing/org-test")
(require 'org-agenda)
(eval-and-compile (require 'cl-lib))
2019-11-12 13:44:05 +00:00
;; General auxiliaries
(defun org-test-agenda--agenda-buffers ()
"Return agenda buffers in a list."
(cl-remove-if-not (lambda (x)
(with-current-buffer x
(eq major-mode 'org-agenda-mode)))
(buffer-list)))
(defun org-test-agenda--kill-all-agendas ()
"Kill all agenda buffers."
(mapc #'kill-buffer
(org-test-agenda--agenda-buffers)))
(defmacro org-test-agenda-with-agenda (text &rest body)
(declare (indent 1))
`(org-test-with-temp-text-in-file ,text
(let ((org-agenda-files `(,buffer-file-name)))
,@body
(org-test-agenda--kill-all-agendas))))
;; Test the Agenda
(ert-deftest test-org-agenda/empty ()
"Empty agenda."
(cl-assert (not org-agenda-sticky) nil "precondition violation")
(cl-assert (not (org-test-agenda--agenda-buffers))
nil "precondition violation")
(let ((org-agenda-span 'day)
org-agenda-files)
(org-agenda-list)
(set-buffer org-agenda-buffer-name)
(should (= 2 (count-lines (point-min) (point-max)))))
(org-test-agenda--kill-all-agendas))
(ert-deftest test-org-agenda/one-line ()
"One informative line in the agenda."
(cl-assert (not org-agenda-sticky) nil "precondition violation")
(cl-assert (not (org-test-agenda--agenda-buffers))
nil "precondition violation")
(let ((org-agenda-span 'day)
(org-agenda-files `(,(expand-file-name "examples/agenda-file.org"
org-test-dir))))
;; NOTE: Be aware that `org-agenda-list' may or may not display
;; past scheduled items depending whether the date is today
;; `org-today' or not.
(org-agenda-list nil "<2017-03-10 Fri>")
(set-buffer org-agenda-buffer-name)
(should (= 3 (count-lines (point-min) (point-max)))))
(org-test-agenda--kill-all-agendas))
(ert-deftest test-org-agenda/time-grid ()
"Test time grid settings."
(cl-assert (not org-agenda-sticky) nil "precondition violation")
(cl-assert (not (org-test-agenda--agenda-buffers))
nil "precondition violation")
;; Default time grid.
(org-test-at-time "2024-01-17 8:00"
(let ((org-agenda-span 'day)
(org-agenda-files `(,(expand-file-name "examples/agenda-file2.org"
org-test-dir))))
;; NOTE: Be aware that `org-agenda-list' may or may not display
;; past scheduled items depending whether the date is today
;; `org-today' or not.
(org-agenda-list nil "<2024-01-17 Fri>")
(set-buffer org-agenda-buffer-name)
(save-excursion
(goto-char (point-min))
(should (search-forward "8:00...... now - - - - - - - - - - - - - - - - - - - - - - - - -")))
(save-excursion
(goto-char (point-min))
(should (search-forward "agenda-file2: 9:30-10:00 Scheduled: TODO one")))
(save-excursion
(goto-char (point-min))
(should (search-forward "agenda-file2:10:00-12:30 Scheduled: TODO two")))
(save-excursion
(goto-char (point-min))
(should (search-forward "10:00...... ----------------")))
(save-excursion
(goto-char (point-min))
(should (search-forward "agenda-file2:13:00-15:00 Scheduled: TODO three")))
(save-excursion
(goto-char (point-min))
(should (search-forward "agenda-file2:19:00...... Scheduled: TODO four"))))
(org-test-agenda--kill-all-agendas))
;; Custom time grid strings
(org-test-at-time "2024-01-17 8:00"
(let ((org-agenda-span 'day)
(org-agenda-files `(,(expand-file-name "examples/agenda-file2.org"
org-test-dir)))
(org-agenda-time-grid '((daily today require-timed)
(800 1000 1200 1400 1600 1800 2000)
"..." "^^^^^^^^^^^^^^" )))
;; NOTE: Be aware that `org-agenda-list' may or may not display
;; past scheduled items depending whether the date is today
;; `org-today' or not.
(org-agenda-list nil "<2024-01-17 Fri>")
(set-buffer org-agenda-buffer-name)
(save-excursion
(goto-char (point-min))
(should (search-forward "10:00... ^^^^^^^^^^^^^^"))))
(org-test-agenda--kill-all-agendas))
;; Time grid remove-match
(org-test-at-time "2024-01-17 8:00"
(let ((org-agenda-span 'day)
(org-agenda-files `(,(expand-file-name "examples/agenda-file2.org"
org-test-dir)))
(org-agenda-time-grid '((today remove-match)
(800 1000 1200 1400 1600 1800 2000)
"......" "----------------" )))
;; NOTE: Be aware that `org-agenda-list' may or may not display
;; past scheduled items depending whether the date is today
;; `org-today' or not.
(org-agenda-list nil "<2024-01-17 Fri>")
(set-buffer org-agenda-buffer-name)
(save-excursion
(goto-char (point-min))
(should (search-forward "agenda-file2: 9:30-10:00 Scheduled: TODO one")))
(save-excursion
(goto-char (point-min))
(should-not (search-forward "10:00...... ----------------" nil t))))
(org-test-agenda--kill-all-agendas))
;; Time grid with `org-agenda-default-appointment-duration'
(org-test-at-time "2024-01-17 8:00"
(let ((org-agenda-span 'day)
(org-agenda-files `(,(expand-file-name "examples/agenda-file2.org"
org-test-dir)))
(org-agenda-time-grid '((today remove-match)
(800 1000 1200 1400 1600 1800 2000)
"......" "----------------" ))
(org-agenda-default-appointment-duration 60))
;; NOTE: Be aware that `org-agenda-list' may or may not display
;; past scheduled items depending whether the date is today
;; `org-today' or not.
(org-agenda-list nil "<2024-01-17 Fri>")
(set-buffer org-agenda-buffer-name)
(save-excursion
(goto-char (point-min))
(should (search-forward "agenda-file2:19:00-20:00 Scheduled: TODO four")))
;; Bug https://list.orgmode.org/orgmode/20211119135325.7f3f85a9@hsu-hh.de/
(save-excursion
(goto-char (point-min))
(should (search-forward "14:00...... ----------------"))))
(org-test-agenda--kill-all-agendas)))
(ert-deftest test-org-agenda/todo-selector ()
"Test selecting keywords in `org-todo-list'."
(cl-assert (not org-agenda-sticky) nil "precondition violation")
(cl-assert (not (org-test-agenda--agenda-buffers))
nil "precondition violation")
(let ((org-todo-keywords
'((sequence "[ ]" "[X]")
(sequence "TODO" "NEXT" "|" "DONE"))))
(org-test-agenda-with-agenda
"
* [ ] Unchecked and will appear in agenda
* NEXT NEXT will appear in agenda
* [X] Checked and will not appear in agenda
* TODO Todo and will appear in agenda
* DONE Done and will not appear in agenda
"
;; All todo keywords.
(org-todo-list)
(set-buffer org-agenda-buffer-name)
(should
(progn
"all todo"
(goto-char (point-min))
(search-forward "[ ] Unchecked and will appear in agenda" nil t)))
(should
(progn
"all todo"
(goto-char (point-min))
(search-forward "NEXT NEXT will appear in agenda" nil t)))
(should
(progn
"all todo"
(goto-char (point-min))
(search-forward "TODO Todo and will appear in agenda" nil t)))
;; All todo keywords, including not done.
(org-todo-list "*")
(should
(progn
"all keywords"
(goto-char (point-min))
(search-forward "[ ] Unchecked and will appear in agenda" nil t)))
(should
(progn
"all keywords"
(goto-char (point-min))
(search-forward "[X] Checked and will not appear in agenda" nil t)))
(should
(progn
"all keywords"
(goto-char (point-min))
(search-forward "DONE Done and will not appear in agenda" nil t)))
(should
(progn
"all keywords"
(goto-char (point-min))
(search-forward "NEXT NEXT will appear in agenda" nil t)))
(should
(progn
"all keywords"
(goto-char (point-min))
(search-forward "TODO Todo and will appear in agenda" nil t)))
;; Just [ ] regexp-like entry.
(org-todo-list "[ ]")
(should
(progn
"[ ] keyword"
(goto-char (point-min))
(search-forward "[ ] Unchecked and will appear in agenda" nil t)))
(should-not
(progn
"[ ] keyword"
(goto-char (point-min))
(search-forward "NEXT NEXT will appear in agenda" nil t)))
(should-not
(progn
"[ ] keyword"
(goto-char (point-min))
(search-forward "TODO Todo and will appear in agenda" nil t)))
;; Two keywords.
(org-todo-list "NEXT|TODO")
(should-not
(progn
"NEXT|TODO"
(goto-char (point-min))
(search-forward "[ ] Unchecked and will appear in agenda" nil t)))
(should
(progn
"NEXT|TODO"
(goto-char (point-min))
(search-forward "NEXT NEXT will appear in agenda" nil t)))
(should
(progn
"NEXT|TODO"
(goto-char (point-min))
(search-forward "TODO Todo and will appear in agenda" nil t))))))
(ert-deftest test-org-agenda/scheduled-non-todo ()
"One informative line in the agenda from scheduled non-todo-keyword-item."
(cl-assert (not org-agenda-sticky) nil "precondition violation")
(cl-assert (not (org-test-agenda--agenda-buffers))
nil "precondition violation")
(dolist (org-element-use-cache '(t nil))
(let ((org-agenda-span 'day)
(org-agenda-files `(,(expand-file-name "examples/agenda-file.org"
org-test-dir))))
;; NOTE: Be aware that `org-agenda-list' may or may not display
;; past scheduled items depending whether the date is today
;; `org-today' or not.
(org-agenda-list nil "<2017-07-19 Wed>")
(set-buffer org-agenda-buffer-name)
(should
(progn (goto-line 3)
(looking-at " *agenda-file:Scheduled: *test agenda"))))
(org-test-agenda--kill-all-agendas)))
(ert-deftest test-org-agenda/non-scheduled-re-matches ()
"Make sure that scheduled-looking elements do not appear in agenda.
See https://list.orgmode.org/20220101200103.GB29829@itccanarias.org/T/#t."
(cl-assert (not org-agenda-sticky) nil "precondition violation")
(cl-assert (not (org-test-agenda--agenda-buffers))
nil "precondition violation")
(let ((org-agenda-span 'day)
(org-agenda-files `(,(expand-file-name "examples/agenda-file.org"
org-test-dir))))
;; NOTE: Be aware that `org-agenda-list' may or may not display
;; past scheduled items depending whether the date is today
;; `org-today' or not.
(org-agenda-list nil "<2022-01-03 Mon>")
(set-buffer org-agenda-buffer-name)
(should (= 2 (count-lines (point-min) (point-max)))))
(org-test-agenda--kill-all-agendas))
(ert-deftest test-org-agenda/org-search-view ()
"Test `org-search-view' specifications."
(cl-assert (not org-agenda-sticky) nil "precondition violation")
(cl-assert (not (org-test-agenda--agenda-buffers))
nil "precondition violation")
;; Search a string.
(let ((org-agenda-files `(,(expand-file-name "examples/agenda-search.org"
org-test-dir))))
(org-search-view nil "foo")
(set-buffer org-agenda-buffer-name)
(should (= 4 (count-lines (point-min) (point-max)))))
;; Search past inlinetask.
(let ((org-agenda-files `(,(expand-file-name "examples/agenda-search.org"
org-test-dir))))
(org-search-view nil "bar")
(set-buffer org-agenda-buffer-name)
(should (= 3 (count-lines (point-min) (point-max)))))
;; Search inside inlinetask.
(let ((org-agenda-files `(,(expand-file-name "examples/agenda-search.org"
org-test-dir))))
(org-search-view nil "text inside inlinetask")
(set-buffer org-agenda-buffer-name)
(should (= 3 (count-lines (point-min) (point-max)))))
(org-test-agenda--kill-all-agendas))
(ert-deftest test-org-agenda/property-timestamp ()
"Match timestamps inside property drawer.
See https://list.orgmode.org/06d301d83d9e$f8b44340$ea1cc9c0$@tomdavey.com"
(cl-assert (not org-agenda-sticky) nil "precondition violation")
(cl-assert (not (org-test-agenda--agenda-buffers))
nil "precondition violation")
(let ((org-agenda-span 'day)
(org-agenda-files `(,(expand-file-name "examples/agenda-file.org"
org-test-dir))))
;; NOTE: Be aware that `org-agenda-list' may or may not display
;; past scheduled items depending whether the date is today
;; `org-today' or not.
(org-agenda-list nil "<2022-03-22 Tue>")
(set-buffer org-agenda-buffer-name)
(should (= 3 (count-lines (point-min) (point-max))))
;; NOTE: Be aware that `org-agenda-list' may or may not display
;; past scheduled items depending whether the date is today
;; `org-today' or not.
(org-agenda-list nil "<2022-03-25 Fri>")
(set-buffer org-agenda-buffer-name)
(should (= 3 (count-lines (point-min) (point-max)))))
(org-test-agenda--kill-all-agendas))
(ert-deftest test-org-agenda/set-priority ()
"One informative line in the agenda. Check that org-agenda-priority updates the agenda."
(cl-assert (not org-agenda-sticky) nil "precondition violation")
(cl-assert (not (org-test-agenda--agenda-buffers))
nil "precondition violation")
(let ((org-agenda-span 'day)
(org-agenda-files `(,(expand-file-name "examples/agenda-file.org"
org-test-dir))))
;; NOTE: Be aware that `org-agenda-list' may or may not display
;; past scheduled items depending whether the date is today
;; `org-today' or not.
(org-agenda-list nil "<2017-07-19 Wed>")
(set-buffer org-agenda-buffer-name)
(should
(progn (goto-line 3)
(org-agenda-priority ?B)
(looking-at-p " *agenda-file:Scheduled: *\\[#B\\] test agenda"))))
(org-test-agenda--kill-all-agendas))
(ert-deftest test-org-agenda/sticky-agenda-name ()
"Agenda buffer name after having created one sticky agenda buffer."
(cl-assert (not org-agenda-sticky) nil "precondition violation")
(cl-assert (not (org-test-agenda--agenda-buffers))
nil "precondition violation")
(let ((org-agenda-span 'day)
(buf (get-buffer org-agenda-buffer-name))
org-agenda-files)
(when buf (kill-buffer buf))
(dolist (fn '(org-agenda-list org-todo-list))
(org-test-with-temp-text "<2017-03-17 Fri>"
(org-follow-timestamp-link)) ;creates a sticky agenda
(org-test-agenda--kill-all-agendas)
(funcall fn)
(should (= 1 (length (org-test-agenda--agenda-buffers))))
(should (string= "*Org Agenda*"
(buffer-name (car (org-test-agenda--agenda-buffers)))))))
(org-test-agenda--kill-all-agendas))
(ert-deftest test-org-agenda/sticky-agenda-name-after-reload ()
"Agenda buffer name of sticky agenda after reload."
(cl-assert (not org-agenda-sticky) nil "precondition violation")
(cl-assert (not (org-test-agenda--agenda-buffers))
nil "precondition violation")
(org-toggle-sticky-agenda)
(let (org-agenda-files)
(org-agenda-list)
(let* ((agenda-buffer-name
(progn
(cl-assert (= 1 (length (org-test-agenda--agenda-buffers))))
(buffer-name (car (org-test-agenda--agenda-buffers))))))
(set-buffer agenda-buffer-name)
(org-agenda-redo)
(should (= 1 (length (org-test-agenda--agenda-buffers))))
(should (string= agenda-buffer-name
(buffer-name (car (org-test-agenda--agenda-buffers)))))))
(org-toggle-sticky-agenda)
(org-test-agenda--kill-all-agendas))
(ert-deftest test-org-agenda/sticky-agenda-filter-preset ()
"Update sticky agenda buffers properly with preset of filters."
(unless org-agenda-sticky
(org-toggle-sticky-agenda))
(org-test-agenda-with-agenda "* TODO Foo"
(org-set-property "CATEGORY" "foo")
(let ((org-agenda-custom-commands
'(("f" "foo: multi-command"
((tags-todo "+CATEGORY=\"foo\"")
(alltodo ""))
((org-agenda-category-filter-preset '("+foo"))))
("b" "bar: multi-command"
((tags-todo "+CATEGORY=\"bar\"")
(alltodo ""))
((org-agenda-category-filter-preset '("+bar"))))
("f1" "foo: single-command"
tags-todo "+CATEGORY=\"foo\""
((org-agenda-category-filter-preset '("+foo"))))
("b1" "bar: single-command"
tags-todo "+CATEGORY=\"bar\""
((org-agenda-category-filter-preset '("+bar"))))
("f2" "foo: single-command"
alltodo "" ((org-agenda-category-filter-preset '("+foo"))))
("b2" "bar: single-command"
alltodo "" ((org-agenda-category-filter-preset '("+bar")))))))
(org-agenda nil "f")
(org-agenda nil "b")
(set-buffer "*Org Agenda(f)*")
(org-agenda-redo)
(goto-char (point-min))
(should (not (invisible-p (1- (search-forward "TODO Foo")))))
(org-test-agenda--kill-all-agendas)
(org-agenda nil "f1")
(org-agenda nil "b1")
(set-buffer "*Org Agenda(f1:+CATEGORY=\"foo\")*")
(org-agenda-redo)
(goto-char (point-min))
(should (not (invisible-p (1- (search-forward "TODO Foo")))))
(org-test-agenda--kill-all-agendas)
(org-agenda nil "f2")
(org-agenda nil "b2")
(set-buffer "*Org Agenda(f2)*")
(org-agenda-redo)
(goto-char (point-min))
(should (not (invisible-p (1- (search-forward "TODO Foo")))))))
(org-toggle-sticky-agenda))
(ert-deftest test-org-agenda/skip-if ()
"Test `org-agenda-skip-if'."
(dolist (options '((scheduled) (notscheduled)
(deadline) (notdeadline)
(timestamp) (nottimestamp)
(regexp "hello") (notregexp "hello")
;; TODO: Test for specific TODO keywords
(todo ("*")) (nottodo ("*"))))
(should
(equal
(if (memq (car options) '(notscheduled notdeadline nottimestamp regexp nottodo))
8
nil)
(org-test-with-temp-text
"* hello"
(org-agenda-skip-if nil options))))
(should
(equal
(if (memq (car options) '(scheduled notdeadline timestamp regexp nottodo))
36
nil)
(org-test-with-temp-text
"* hello
SCHEDULED: <2023-07-15 Sat>"
(org-agenda-skip-if nil options))))
(should
(equal
(if (memq (car options) '(notscheduled deadline timestamp regexp nottodo))
35
nil)
(org-test-with-temp-text
"* hello
DEADLINE: <2023-07-15 Sat>"
(org-agenda-skip-if nil options))))
(should
(equal
(if (memq (car options) '(notscheduled notdeadline timestamp regexp nottodo))
25
nil)
(org-test-with-temp-text
"* hello
<2023-07-15 Sat>"
(org-agenda-skip-if nil options))))
(should
(equal
(if (memq (car options) '(notscheduled notdeadline nottimestamp notregexp nottodo))
10
nil)
(org-test-with-temp-text
"* goodbye"
(org-agenda-skip-if nil options))))
(should
(equal
(if (memq (car options) '(notscheduled notdeadline nottimestamp notregexp todo))
26
nil)
(org-test-with-temp-text
"* TODO write better tests"
(org-agenda-skip-if nil options))))))
(ert-deftest test-org-agenda/timestamp-ignore-todo-item ()
"Test if `org-agenda' ignores a todo item with a timestamp.
Based on the following variables:
`org-agenda-todo-ignore-deadlines',
`org-agenda-todo-ignore-scheduled', and
`org-agenda-todo-ignore-timestamp'."
;; TODO: test `org-agenda-todo-ignore-with-date'.
;; Maybe test having multiple variables set.
(let ((org-agenda-custom-commands
'(("f" "no fluff" todo ""
((org-agenda-todo-keyword-format "")
(org-agenda-overriding-header "")
(org-agenda-prefix-format "")))))
(org-deadline-warning-days 1)
(expected-return
(lambda (timestamp value)
(cl-case timestamp
(past (memq value '(all near past -1 -2 -3)))
(yesteryesterday (memq value '(all near past -1 -2)))
(yesterday (memq value '(all near past -1)))
(today (memq value '(all near past 0)))
(tomorrow (memq value '(all near future 0 1)))
(tomorroworrow (memq value '(all far future 0 1 2)))
(future (memq value '(all far future 0 1 2 3))))))
;; Lexically bind the variables we're changing
org-agenda-todo-ignore-deadlines
org-agenda-todo-ignore-scheduled
org-agenda-todo-ignore-timestamp)
(org-test-at-time "2023-01-15"
(dolist (variable '(org-agenda-todo-ignore-deadlines
org-agenda-todo-ignore-scheduled
org-agenda-todo-ignore-timestamp))
(dolist (type '(timestamp scheduled deadline))
;; nil is last so it resets the variable for the next one
(dolist (value `(past future all 3 2 1 0 -1 -2 -3
,@(when (eq type 'deadline) '(near far nil))))
(dolist (timestamp '((past . "<2023-01-01>")
(yesteryesterday . "<2023-01-13>")
(yesterday . "<2023-01-14>")
(today . "<2023-01-15>")
(tomorrow . "<2023-01-16>")
(tomorroworrow . "<2023-01-17>")
(future . "<2023-01-31>")))
;; Uncomment to debug failure
;; (message "Type: %S, Variable: %S, Value: %S, Time: %S" type variable value (car timestamp))
(set variable value)
(org-test-agenda-with-agenda
(cl-case type
(timestamp (concat "* TODO hello\n" (cdr timestamp)))
(scheduled (concat "* TODO hello
SCHEDULED: " (cdr timestamp)))
(deadline (concat "* TODO hello
DEADLINE: " (cdr timestamp))))
(should
(string-equal
(or (and (funcall expected-return (car timestamp) value)
(cl-case variable
(org-agenda-todo-ignore-deadlines
(eq type 'deadline))
(org-agenda-todo-ignore-scheduled
(eq type 'scheduled))
(org-agenda-todo-ignore-timestamp
(eq type 'timestamp)))
"")
"hello\n")
(progn
(org-agenda nil "f")
(buffer-string))))))))))))
(ert-deftest test-org-agenda/skip-scheduled-repeats-after-deadline ()
"Test `org-agenda-skip-scheduled-repeats-after-deadline'."
(cl-assert (not org-agenda-sticky) nil "precondition violation")
(cl-assert (not (org-test-agenda--agenda-buffers))
nil "precondition violation")
(dolist (org-agenda-skip-scheduled-repeats-after-deadline '(nil t))
(org-test-at-time "2024-01-01 8:00"
(org-test-with-temp-text-in-file "
* TODO Do me every day before Jan, 12th (included)
SCHEDULED: <2024-01-03 Wed +1d> DEADLINE: <2024-01-05 Fri>
"
(let ((org-agenda-span 'week)
(org-agenda-files `(,(buffer-file-name))))
;; NOTE: Be aware that `org-agenda-list' may or may not display
;; past scheduled items depending whether the date is today
;; `org-today' or not.
(org-agenda-list nil "<2024-01-01 Mon>")
(set-buffer org-agenda-buffer-name)
(if org-agenda-skip-scheduled-repeats-after-deadline
(should
;; Not displayed after deadline.
(string-match-p
"Week-agenda (W01):
Monday 1 January 2024 W01
[^:]+:In 4 d.: TODO Do me every day before Jan, 12th (included)
Tuesday 2 January 2024
Wednesday 3 January 2024
[^:]+:Scheduled: TODO Do me every day before Jan, 12th (included)
Thursday 4 January 2024
[^:]+:Scheduled: TODO Do me every day before Jan, 12th (included)
Friday 5 January 2024
[^:]+:Scheduled: TODO Do me every day before Jan, 12th (included)
[^:]+:Deadline: TODO Do me every day before Jan, 12th (included)
Saturday 6 January 2024
Sunday 7 January 2024"
(buffer-string)))
(should
;; Displayed after deadline.
(string-match-p
"Week-agenda (W01):
Monday 1 January 2024 W01
[^:]+:In 4 d.: TODO Do me every day before Jan, 12th (included)
Tuesday 2 January 2024
Wednesday 3 January 2024
[^:]+:Scheduled: TODO Do me every day before Jan, 12th (included)
Thursday 4 January 2024
[^:]+:Scheduled: TODO Do me every day before Jan, 12th (included)
Friday 5 January 2024
[^:]+:Scheduled: TODO Do me every day before Jan, 12th (included)
[^:]+:Deadline: TODO Do me every day before Jan, 12th (included)
Saturday 6 January 2024
[^:]+:Scheduled: TODO Do me every day before Jan, 12th (included)
Sunday 7 January 2024
[^:]+:Scheduled: TODO Do me every day before Jan, 12th (included)"
(buffer-string))))))
(org-test-agenda--kill-all-agendas))))
(ert-deftest test-org-agenda/goto-date ()
"Test `org-agenda-goto-date'."
(unwind-protect
(should
(equal
(time-to-days (org-time-string-to-time "2019-12-30"))
(let ((org-agenda-files nil))
(org-agenda-list nil nil 'day)
(org-agenda-goto-date "2019-12-30")
(get-text-property (point) 'day))))
(org-test-agenda--kill-all-agendas)))
(ert-deftest test-org-agenda/file-restriction ()
"Test file restriction for org agenda."
(org-test-with-temp-text-in-file "* TODO Foo"
(org-agenda-set-restriction-lock t)
(org-agenda nil "t")
(should (search-forward "Foo"))
(should (org-agenda-files))
(should-not (org-agenda-files t))
(org-agenda-remove-restriction-lock)
(goto-char (point-min))
(should-not (search-forward "Foo" nil t))
(should-not (org-agenda-files)))
(org-test-with-temp-text-in-file "* TODO Bar"
(org-agenda nil "t" 'buffer)
(should (search-forward "Bar"))
(should (org-agenda-files))
(should-not (org-agenda-files t))
(org-agenda-remove-restriction-lock)
(goto-char (point-min))
(should-not (search-forward "Bar" nil t))
(should-not (org-agenda-files)))
(org-test-with-temp-text-in-file "* TODO Bar"
(org-agenda nil "t" 'buffer)
(org-agenda nil "t")
(should-not (search-forward "Bar" nil t))
(should-not (org-agenda-files)))
(org-test-agenda--kill-all-agendas))
(ert-deftest test-org-agenda/skip-deadline-prewarning-if-scheduled ()
"Test `org-agenda-skip-deadline-prewarning-if-scheduled'."
(org-test-at-time
"2024-01-15"
(let ((org-agenda-skip-deadline-prewarning-if-scheduled t))
(org-test-agenda-with-agenda
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
(org-agenda-list nil nil 1)
(should-not (search-forward "In " nil t))))
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 10))
(org-test-agenda-with-agenda
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
(org-agenda-list nil nil 1)
(should (search-forward "In " nil t))))
;; Custom prewarning cookie "-3d", so there should be no warning anyway.
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 10))
(org-test-agenda-with-agenda
"* TODO foo\nDEADLINE: <2024-01-20 Sat -3d> SCHEDULED: <2024-01-19 Fri>"
(org-agenda-list nil nil 1)
(should-not (search-forward "In " nil t))))
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 3))
(org-test-agenda-with-agenda
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
(org-agenda-list nil nil 1)
(should-not (search-forward "In " nil t))))
(let ((org-agenda-skip-deadline-prewarning-if-scheduled nil))
(org-test-agenda-with-agenda
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
(org-agenda-list nil nil 1)
(should (search-forward "In " nil t))))
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled))
(org-test-agenda-with-agenda
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-16 Tue>"
(org-agenda-list nil nil 1)
(should-not (search-forward "In " nil t))))
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled))
(org-test-agenda-with-agenda
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-15 Mon>"
(org-agenda-list nil nil 1)
(should (search-forward "In " nil t))))))
(ert-deftest test-org-agenda/diary-timestamp ()
"Test diary timestamp handling."
(org-test-at-time
"2024-01-15"
(org-test-agenda-with-agenda
"* TODO foo\n<%%(diary-date 01 15 2024)>"
(org-agenda-list nil nil 1)
(should (search-forward "foo" nil t)))
(org-test-agenda-with-agenda
"* TODO foo\n<%%(diary-date 02 15 2024)>"
(org-agenda-list nil nil 1)
(should-not (search-forward "foo" nil t)))
;; Test time and time ranges in diary timestamps.
(org-test-agenda-with-agenda
"* TODO foo\n<%%(diary-date 01 15 2024) 12:00>"
(org-agenda-list nil nil 1)
(should (search-forward "12:00" nil t)))
(org-test-agenda-with-agenda
"* TODO foo\n<%%(diary-date 01 15 2024) 12:00-14:00>"
(org-agenda-list nil nil 1)
(should (search-forward "12:00-14:00" nil t)))))
;; agenda redo
(require 'face-remap)
(ert-deftest test-org-agenda/rescale ()
"Text scale survives `org-agenda-redo'."
(org-test-agenda--kill-all-agendas)
(unwind-protect
(let ((org-agenda-span 'day)
org-agenda-files)
(org-agenda-list)
(set-buffer org-agenda-buffer-name)
(text-scale-mode)
(text-scale-set 11)
(cl-assert (and (boundp text-scale-mode) text-scale-mode))
(org-agenda-redo)
(should text-scale-mode)
(should (= 11 text-scale-mode-amount)))
(org-test-agenda--kill-all-agendas)))
(ert-deftest test-org-agenda/redo-setting ()
"Command settings survives `org-agenda-redo'."
(org-test-agenda--kill-all-agendas)
(let ((org-agenda-custom-commands
'(("t" "TODOs" alltodo ""
((org-agenda-overriding-header "Test"))))))
(org-agenda nil "t")
(org-agenda-redo)
(org-agenda-redo)
(goto-char (point-min))
(should (looking-at-p "Test")))
(org-test-agenda--kill-all-agendas))
(ert-deftest test-org-agenda/diary-inclusion ()
"Diary inclusion happens."
(org-test-agenda--kill-all-agendas)
(let ((diary-file (expand-file-name "examples/diary-file" org-test-dir))
(org-agenda-files `(,(expand-file-name "examples/agenda-file.org"
org-test-dir)))
(diary-date-forms '((month "[-/]" day "[^-/0-9]")
(year "[-/]" month "[-/]" day "[^0-9]")
(monthname " *" day "[^-0-9]")
(year " *" monthname " *" day "[^0-9]")
(dayname "\\W")))
(org-agenda-span 'day)
(org-agenda-include-diary t))
;; NOTE: Be aware that `org-agenda-list' may or may not display
;; past scheduled items depending whether the date is today
;; `org-today' or not.
(org-agenda-list nil "<2019-01-08>")
(should (search-forward "f0bcf0cd8bad93c1451bb6e1b2aaedef5cce7cbb" nil t))
(org-test-agenda--kill-all-agendas)))
;; agenda bulk actions
(ert-deftest test-org-agenda/bulk ()
"Bulk actions are applied to marked items."
(org-test-agenda-with-agenda "* TODO a\n* TODO b"
(org-todo-list)
(org-agenda-bulk-mark-all)
(cl-letf (((symbol-function 'read-char-exclusive)
(lambda () ?t))
((symbol-function 'completing-read)
testing: Make all files use `lexical-binding` Mainly, add the corresponding cookie, but also add various `require`s so that the compiler knows which vars should be trated as dynbound. This does not fix all the warnings, but does try to eliminate all those about "unused" variables. For the variables truly unused, the patch usually adds an underscore to their name to silence the warning. Some of the fixes affect files which already used `lexical-binding`. Not sure why the test worked before: maybe because the tests were run without compiling them first (which could cause some of the missing `require`d packages to be autoloaded before we got to the problematic code, thus hiding the problem)? I found some suspicious code, for which I added FIXMEs. There are also a few changes to the main files. * lisp/org-protocol.el (org-protocol-check-filename-for-protocol): Don't call `server-edit` if it's not yet defined. [ Needed to get the tests to pass. ] * lisp/ob-core.el (org-babel-temporary-directory) (org-babel-temporary-stable-directory): Always define (and use nil if we don't want to create a directory for it). Simplify the code based on the fact that (defvar V E) only evaluates E if V is not yet `boundp`. (org-babel-temp-file, org-babel-temp-stable-file) (org-babel-remove-temporary-directory) (org-babel-remove-temporary-stable-directory): Adjust accordingly. * lisp/org.el (org-log-beginning): Add FIXME. * testing/org-test.el: Require `org` and `org-id`. (org-id-locations-file): Don't `defconst` it. (org-test-at-id, org-test-in-example-file, org-test-at-marker) (org-test-with-temp-text, org-test-with-temp-text-in-file): Move edebug specs into `declare` (and simplify them). (org-test-with-tramp-remote-dir--worker): Declare dynbound tramp vars. (org--compile-when): Fix quoting of `exp`. (org-test-load): Tweak regexps. * testing/org-batch-test-init.el: Tweak regexp, remove dead code and add a FIXME about it. * testing/lisp/test-ox.el: Require `ox` instead of erroring out if it's not already loaded. Also require `org-inlinetask`. (org-test-with-parsed-data): Silence warnings when `info` is not used. (test-org-export/bind-keyword): Add FIXME. * testing/lisp/test-ox-publish.el: Require `org-test` and `ox-publish`. (test-org-publish/resolve-external-link): Expose lambdas to the compiler. Remove unused var `ids`. (test-org-publish/get-project-from-filename): Remove unused var `file`. * testing/lisp/test-org.el: Require `org-macs`, `org`, `org-inlinetask`, `org-refile`, and `org-agenda`. (test-org/org-read-date): Declare `org-time-was-given` as dynbound. (test-org/set-regexps-and-options): Add FIXME. * testing/lisp/test-org-timer.el: Require `org-timer`. * testing/lisp/test-org-table.el: Require `ox`. * testing/lisp/test-org-protocol.el: Require `org-protocol` instead of erroring out if it's not already loaded. Also require `capture`, and add missing `provide` statement. * testing/lisp/test-org-pcomplete.el: Require `org`. * testing/lisp/test-org-list.el: Require `org-list` and `org`. * testing/lisp/test-org-lint.el: Require `org-footnote` and `org-lint`. * testing/lisp/test-org-footnote.el: Require `org-footnote`. * testing/lisp/test-org-element.el: Require `org-element` instead of erroring out if it's not already loaded. Also require `org` and `org-inlinetask`. * testing/lisp/test-org-duration.el: Require `org-duration`. * testing/lisp/test-org-datetree.el: Require `org-datetree`. * testing/lisp/test-org-colview.el: Require `org-colview`, `org-duration`, and `org-inlinetask`. * testing/lisp/test-org-clock.el: Require `org-duration` and `org-clock`. * testing/lisp/test-org-archive.el: Require `org-archive`. * testing/lisp/test-org-agenda.el (test-org-agenda/bulk-custom-arg-func): Add FIXME. * testing/lisp/test-ol.el: Require `ol` and `org-id`. (test-org-link/store-link): Declare `org-store-link-props` and add FIXME. * testing/lisp/test-oc.el (test-org-cite/export-capability): Add FIXME. * testing/lisp/test-ob.el: Require `ob-core`, `org-src`, `ob-ref`, and `org-table`. (test-ob/eval-header-argument): Rename `foo` to `test-ob--foo` and declare it as dynbound. (test-ob/blocks-with-spaces, test-ob/specific-colnames): Add FIXME. (test-ob/noweb-expansions-in-cache): Declare `noweb-expansions-in-cache-var` as dynbound. * testing/lisp/test-ob-tangle.el: Require `org` and `ob-tangle`. * testing/lisp/test-ob-shell.el: * testing/lisp/test-ob-python.el: Require `ob-core`. * testing/lisp/test-ob-lob.el: Require `ob-lob`. (temporary-value-for-test): Declare as dynbound. * testing/lisp/test-ob-plantuml.el: Require `ob-plantuml` instead of erroring out if it's not already loaded. * testing/lisp/test-ob-lilypond.el: Require `ob-lilypond` instead of erroring out if it's not already loaded. Use `with-current-buffer`. * testing/lisp/test-ob-julia.el: Require `ob-core`. * testing/lisp/test-ob-java.el (org-babel-temporary-directory): Remove dead code now that `org-babel-temporary-directory` is always bound. * testing/lisp/test-ob-exp.el: Require `ob-exp`, `org-src`, and `org-test`. (ob-exp/evaluate-all-executables-in-order): Declare `*evaluation-collector*` as dynbound. * testing/lisp/test-ob-emacs-lisp.el (ob-emacs-lisp/dynamic-lexical-edit) (ob-emacs-lisp/dynamic-lexical-execute): Rename dynbound var to `ob-emacs--x` and declare it as such. * testing/lisp/test-ob-R.el: Require `ob-core`. (ess-ask-for-ess-directory, ess-history-file): Declare vars.
2022-09-14 21:21:37 +00:00
(lambda (&rest _rest) "DONE")))
(org-agenda-bulk-action))
(org-agenda-previous-item 99)
(should (looking-at ".*DONE a"))
(org-agenda-next-item 1)
(should (looking-at ".*DONE b"))))
(ert-deftest test-org-agenda/bulk-custom ()
"Custom bulk actions are applied to all marked items."
(org-test-agenda-with-agenda "* TODO a\n* TODO b"
(org-todo-list)
(org-agenda-bulk-mark-all)
;; Mock read functions
(let* ((f-call-cnt 0)
(org-agenda-bulk-custom-functions
`((?P ,(lambda () (setq f-call-cnt (1+ f-call-cnt)))))))
(cl-letf* (((symbol-function 'read-char-exclusive)
(lambda () ?P)))
(org-agenda-bulk-action)
(should (= f-call-cnt 2))))))
(ert-deftest test-org-agenda/bulk-custom-arg-func ()
"Argument collection functions can be specified for custom bulk
functions."
(org-test-agenda-with-agenda "* TODO a\n* TODO b"
(org-todo-list)
(org-agenda-bulk-mark-all)
(let* ((f-called-cnt 0)
(arg-f-call-cnt 0)
(f-called-args nil)
(org-agenda-bulk-custom-functions
`((?P
;; Custom bulk function
,(lambda (&rest args)
(message "test")
(setq f-called-cnt (1+ f-called-cnt)
f-called-args args))
;; Argument collection function
,(lambda ()
(setq arg-f-call-cnt (1+ arg-f-call-cnt))
'(1 2 3))))))
(cl-letf (((symbol-function 'read-char-exclusive)
(lambda () ?P)))
(org-agenda-bulk-action))
(should (= f-called-cnt 2))
(should (= arg-f-call-cnt 1))
(should (equal f-called-args '(1 2 3))))))
(provide 'test-org-agenda)
;;; test-org-agenda.el ends here