1 ;;;; org-test.el --- Tests for Org-mode
3 ;; Copyright (c) 2010-2012 Sebastian Rose, Eric Schulte
5 ;; Sebastian Rose, Hannover, Germany, sebastian_rose gmx de
6 ;; Eric Schulte, Santa Fe, New Mexico, USA, schulte.eric gmail com
7 ;; David Maus, Brunswick, Germany, dmaus ictsoc de
9 ;; Released under the GNU General Public License version 3
10 ;; see: http://www.gnu.org/licenses/gpl-3.0.html
12 ;; Definition of `special-mode' copied from Emacs23's simple.el to be
13 ;; provide a testing environment for Emacs22.
17 ;; Interactive testing for Org mode.
19 ;; The heart of all this is the commands `org-test-current-defun'. If
20 ;; called while in a `defun' all ert tests with names matching the
21 ;; name of the function are run.
24 ;; For test development purposes a number of navigation and test
25 ;; function construction routines are available as a git submodule
28 ;; $ git submodule init
29 ;; $ git submodule update
33 (let* ((org-test-dir (expand-file-name
35 (or load-file-name buffer-file-name
))))
36 (org-lisp-dir (expand-file-name
37 (concat org-test-dir
"../lisp"))))
39 (unless (featurep 'org
)
40 (setq load-path
(cons org-lisp-dir load-path
))
43 (org-babel-do-load-languages
44 'org-babel-load-languages
'((sh . t
) (org . t
))))
46 (let* ((load-path (cons
49 (expand-file-name "jump" org-test-dir
)
52 (when (= emacs-major-version
22)
53 (defvar special-mode-map
54 (let ((map (make-sparse-keymap)))
56 (define-key map
"q" 'quit-window
)
57 (define-key map
" " 'scroll-up
)
58 (define-key map
"\C-?" 'scroll-down
)
59 (define-key map
"?" 'describe-mode
)
60 (define-key map
"h" 'describe-mode
)
61 (define-key map
">" 'end-of-buffer
)
62 (define-key map
"<" 'beginning-of-buffer
)
63 (define-key map
"g" 'revert-buffer
)
64 (define-key map
"z" 'kill-this-buffer
)
67 (put 'special-mode
'mode-class
'special
)
68 (define-derived-mode special-mode nil
"Special"
69 "Parent major mode from which special major modes should inherit."
70 (setq buffer-read-only t
)))
74 (expand-file-name "jump/jump.el" org-test-dir
))
76 (require 'which-func
))))
78 (defconst org-test-default-test-file-name
"tests.el"
79 "For each defun a separate file with tests may be defined.
80 tests.el is the fallback or default if you like.")
82 (defconst org-test-default-directory-name
"testing"
83 "Basename or the directory where the tests live.
84 org-test searches this directory up the directory tree.")
86 (defconst org-test-dir
87 (expand-file-name (file-name-directory (or load-file-name buffer-file-name
))))
89 (defconst org-base-dir
90 (expand-file-name ".." org-test-dir
))
92 (defconst org-test-example-dir
93 (expand-file-name "examples" org-test-dir
))
95 (defconst org-test-file
96 (expand-file-name "normal.org" org-test-example-dir
))
98 (defconst org-test-no-heading-file
99 (expand-file-name "no-heading.org" org-test-example-dir
))
101 (defconst org-test-link-in-heading-file
102 (expand-file-name "link-in-heading.org" org-test-dir
))
105 ;;; Functions for writing tests
106 (put 'missing-test-dependency
108 '(error missing-test-dependency
))
110 (defun org-test-for-executable (exe)
111 "Throw an error if EXE is not available.
112 This can be used at the top of code-block-language specific test
113 files to avoid loading the file on systems without the
117 (or acc
(file-exists-p (expand-file-name exe dir
))))
118 exec-path
:initial-value nil
)
119 (signal 'missing-test-dependency
(list exe
))))
121 (defun org-test-buffer (&optional file
)
122 "TODO: Setup and return a buffer to work with.
123 If file is non-nil insert it's contents in there.")
125 (defun org-test-compare-with-file (&optional file
)
126 "TODO: Compare the contents of the test buffer with FILE.
127 If file is not given, search for a file named after the test
128 currently executed.")
130 (defmacro org-test-at-id
(id &rest body
)
131 "Run body after placing the point in the headline identified by ID."
133 `(let* ((id-location (org-id-find ,id
))
134 (id-file (car id-location
))
135 (visited-p (get-file-buffer id-file
))
137 (save-window-excursion
140 (setq to-be-removed
(current-buffer))
144 (org-show-block-all))
146 (save-restriction ,@body
)))
148 (kill-buffer to-be-removed
))))
149 (def-edebug-spec org-test-at-id
(form body
))
151 (defmacro org-test-in-example-file
(file &rest body
)
152 "Execute body in the Org-mode example file."
154 `(let* ((my-file (or ,file org-test-file
))
155 (visited-p (get-file-buffer my-file
))
157 (save-window-excursion
160 (unless (eq major-mode
'org-mode
)
162 (setq to-be-removed
(current-buffer))
163 (goto-char (point-min))
166 (outline-next-visible-heading 1)
168 (org-show-block-all))
170 (save-restriction ,@body
)))
172 (kill-buffer to-be-removed
))))
173 (def-edebug-spec org-test-in-example-file
(form body
))
175 (defmacro org-test-at-marker
(file marker
&rest body
)
176 "Run body after placing the point at MARKER in FILE.
177 Note the uuidgen command-line command can be useful for
178 generating unique markers for insertion as anchors into org
181 `(org-test-in-example-file ,file
182 (goto-char (point-min))
183 (re-search-forward (regexp-quote ,marker
))
185 (def-edebug-spec org-test-at-marker
(form form body
))
187 (defmacro org-test-with-temp-text
(text &rest body
)
188 "Run body in a temporary buffer with Org-mode as the active
189 mode holding TEXT. If the string \"<point>\" appears in TEXT
190 then remove it and place the point there before running BODY,
191 otherwise place the point at the beginning of the inserted text."
193 (let ((inside-text (if (stringp text
) text
(eval text
))))
196 ,(let ((point (string-match (regexp-quote "<point>") inside-text
)))
198 `(progn (insert `(replace-match "" nil nil inside-text
))
199 (goto-char ,(match-beginning 0)))
200 `(progn (insert ,inside-text
)
201 (goto-char (point-min)))))
202 (prog1 ,@body
(kill-buffer)))))
203 (def-edebug-spec org-test-with-temp-text
(form body
))
205 (defmacro org-test-with-temp-text-in-file
(text &rest body
)
206 "Run body in a temporary file buffer with Org-mode as the active mode."
208 (let ((file (make-temp-file "org-test"))
209 (inside-text (if (stringp text
) text
(eval text
)))
211 `(let ((kill-buffer-query-functions nil
) ,results
)
212 (with-temp-file ,file
(insert ,inside-text
))
215 (setq ,results
,@body
)
216 (save-buffer) (kill-buffer (current-buffer))
219 (def-edebug-spec org-test-with-temp-text-in-file
(form body
))
222 ;;; Navigation Functions
223 (when (featurep 'jump
)
224 (defjump org-test-jump
225 (("lisp/\\1.el" .
"testing/lisp/test-\\1.el")
226 ("lisp/\\1.el" .
"testing/lisp/\\1.el/test.*.el")
227 ("contrib/lisp/\\1.el" .
"testing/contrib/lisp/test-\\1.el")
228 ("contrib/lisp/\\1.el" .
"testing/contrib/lisp/\\1.el/test.*.el")
229 ("testing/lisp/test-\\1.el" .
"lisp/\\1.el")
230 ("testing/lisp/\\1.el" .
"lisp/\\1.el/test.*.el")
231 ("testing/contrib/lisp/test-\\1.el" .
"contrib/lisp/\\1.el")
232 ("testing/contrib/lisp/test-\\1.el" .
"contrib/lisp/\\1.el/test.*.el"))
233 (concat org-base-dir
"/")
234 "Jump between org-mode files and their tests."
236 (let* ((full-path (expand-file-name path org-base-dir
))
237 (file-name (file-name-nondirectory path
))
238 (name (file-name-sans-extension file-name
)))
239 (find-file full-path
)
241 ";;; " file-name
"\n\n"
242 ";; Copyright (c) " (nth 5 (decode-time (current-time)))
243 " " user-full-name
"\n"
244 ";; Authors: " user-full-name
"\n\n"
245 ";; Released under the GNU General Public License version 3\n"
246 ";; see: http://www.gnu.org/licenses/gpl-3.0.html\n\n"
248 ";; Template test file for Org-mode tests\n\n"
251 "(let ((load-path (cons (expand-file-name\n"
252 " \"..\" (file-name-directory\n"
253 " (or load-file-name buffer-file-name)))\n"
255 " (require 'org-test)\n"
256 " (require 'org-test-ob-consts))\n\n"
259 "(ert-deftest " name
"/example-test ()\n"
260 " \"Just an example to get you started.\"\n"
262 " (should-not nil)\n"
263 " (should-error (error \"errr...\")))\n\n\n"
264 "(provide '" name
")\n\n"
265 ";;; " file-name
" ends here\n") full-path
))
266 (lambda () ((lambda (res) (if (listp res
) (car res
) res
)) (which-function)))))
268 (define-key emacs-lisp-mode-map
"\M-\C-j" 'org-test-jump
)
271 ;;; Miscellaneous helper functions
272 (defun org-test-strip-text-props (s)
273 "Return S without any text properties."
274 (let ((noprop (copy-sequence s
)))
275 (set-text-properties 0 (length noprop
) nil noprop
)
279 (defun org-test-string-exact-match (regex string
&optional start
)
280 "case sensative string-match"
281 (let ((case-fold-search nil
)
283 (if(and (equal regex
"")
284 (not(equal string
"")))
286 (if (equal 0 (string-match regex string start
))
290 ;;; Load and Run tests
291 (defun org-test-load ()
292 "Load up the org-mode test suite."
295 ;; Recursively load all files, if files throw errors
296 ;; then silently ignore the error and continue to the
297 ;; next file. This allows files to error out if
298 ;; required executables aren't available.
301 (if (file-directory-p path
)
304 (when (string-match "^[A-Za-z].*\\.el$"
305 (file-name-nondirectory path
))
307 (missing-test-dependency
309 (concat "org-missing-dependency/"
310 (file-name-nondirectory
311 (file-name-sans-extension path
))))))
312 (eval `(ert-deftest ,name
()
313 :expected-result
:failed
(should nil
))))))))
314 (directory-files base
'full
315 "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*\\.el$"))))
316 (rld (expand-file-name "lisp" org-test-dir
))
317 (rld (expand-file-name "lisp" (expand-file-name "contrib" org-test-dir
)))))
319 (defun org-test-current-defun ()
320 "Test the current function."
322 (ert (which-function)))
324 (defun org-test-current-file ()
325 "Run all tests for current file."
328 (file-name-sans-extension
329 (file-name-nondirectory (buffer-file-name)))
332 (defvar org-test-buffers nil
333 "Hold buffers open for running Org-mode tests.")
335 (defun org-test-touch-all-examples ()
336 (dolist (file (directory-files
337 org-test-example-dir
'full
338 "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*\\.org$"))
339 (unless (get-file-buffer file
)
340 (add-to-list 'org-test-buffers
(find-file file
)))))
342 (defun org-test-kill-all-examples ()
343 (while org-test-buffers
344 (let ((b (pop org-test-buffers
)))
345 (when (buffer-live-p b
) (kill-buffer b
)))))
347 (defun org-test-update-id-locations ()
348 (org-id-update-id-locations
350 org-test-example-dir
'full
351 "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*\\.org$")))
353 (defun org-test-run-batch-tests ()
354 "Run all defined tests matching \"\\(org\\|ob\\)\".
355 Load all test files first."
357 (let ((org-id-track-globally t
)
358 (org-id-locations-file
359 (convert-standard-filename
361 "testing/.test-org-id-locations"
363 (org-test-touch-all-examples)
364 (org-test-update-id-locations)
366 (ert-run-tests-batch-and-exit "\\(org\\|ob\\)")))
368 (defun org-test-run-all-tests ()
369 "Run all defined tests matching \"\\(org\\|ob\\)\".
370 Load all test files first."
372 (org-test-touch-all-examples)
374 (ert "\\(org\\|ob\\)")
375 (org-test-kill-all-examples))
379 ;;; org-test.el ends here