2017-11-05 01:23:36 +01:00
|
|
|
;;; core/autoload/test.el -*- lexical-binding: t; no-byte-compile: t; -*-
|
2017-04-04 22:18:38 -04:00
|
|
|
|
2017-06-24 17:15:22 +02:00
|
|
|
;;;###autoload
|
2017-11-05 01:23:36 +01:00
|
|
|
(defun doom//run-tests (&optional modules)
|
2017-06-24 17:15:22 +02:00
|
|
|
"Run all loaded tests, specified by MODULES (a list of module cons cells) or
|
|
|
|
command line args following a double dash (each arg should be in the
|
|
|
|
'module/submodule' format).
|
|
|
|
|
|
|
|
If neither is available, run all tests in all enabled modules."
|
2017-11-08 22:52:49 +01:00
|
|
|
(interactive)
|
2017-06-24 17:15:22 +02:00
|
|
|
(condition-case-unless-debug ex
|
|
|
|
(let (targets)
|
|
|
|
;; ensure DOOM is initialized
|
2017-08-08 16:31:48 +02:00
|
|
|
(let (noninteractive)
|
2017-08-09 15:30:42 +02:00
|
|
|
(load (expand-file-name "core/core.el" user-emacs-directory) nil t)
|
|
|
|
(doom-initialize-modules nil))
|
2017-06-24 17:15:22 +02:00
|
|
|
;; collect targets
|
2017-11-05 17:16:13 +01:00
|
|
|
(cond ((and argv (equal (car argv) "--"))
|
2017-12-09 14:37:43 -05:00
|
|
|
(cl-loop for arg in (cdr argv)
|
2017-06-24 17:15:22 +02:00
|
|
|
if (equal arg "core")
|
2017-11-05 17:16:13 +01:00
|
|
|
do (push (expand-file-name "test/" doom-core-dir) targets)
|
2017-06-24 17:15:22 +02:00
|
|
|
else
|
2017-11-05 17:16:13 +01:00
|
|
|
collect
|
|
|
|
(cl-destructuring-bind (car &optional cdr) (split-string arg "/" t)
|
|
|
|
(cons (intern (concat ":" car))
|
|
|
|
(and cdr (intern cdr))))
|
|
|
|
into args
|
|
|
|
finally do
|
|
|
|
(setq modules args argv nil)))
|
2017-06-24 17:15:22 +02:00
|
|
|
|
|
|
|
(modules
|
|
|
|
(unless (cl-loop for module in modules
|
|
|
|
unless (and (consp module)
|
|
|
|
(keywordp (car module))
|
|
|
|
(symbolp (cdr module)))
|
|
|
|
return t)
|
|
|
|
(error "Expected a list of cons, got: %s" modules)))
|
|
|
|
|
|
|
|
(t
|
2017-06-28 15:28:43 +02:00
|
|
|
(let ((noninteractive t)
|
|
|
|
doom-modules)
|
|
|
|
(load (expand-file-name "init.test.el" user-emacs-directory) nil t)
|
2017-11-08 22:45:52 +01:00
|
|
|
(setq modules (doom-module-pairs)
|
2017-06-28 15:28:43 +02:00
|
|
|
targets (list (expand-file-name "test/" doom-core-dir))))))
|
2017-06-24 17:15:22 +02:00
|
|
|
;; resolve targets to a list of test files and load them
|
|
|
|
(cl-loop with targets =
|
|
|
|
(append targets
|
|
|
|
(cl-loop for (module . submodule) in modules
|
|
|
|
if submodule
|
|
|
|
collect (doom-module-path module submodule "test/")
|
|
|
|
else
|
|
|
|
nconc
|
|
|
|
(cl-loop with module-name = (substring (symbol-name module) 1)
|
|
|
|
with module-path = (expand-file-name module-name doom-modules-dir)
|
|
|
|
for path in (directory-files module-path t "^\\w")
|
|
|
|
collect (expand-file-name "test/" path))))
|
|
|
|
for dir in targets
|
|
|
|
if (file-directory-p dir)
|
2017-11-05 17:16:13 +01:00
|
|
|
nconc (reverse (directory-files-recursively dir "\\.el$"))
|
|
|
|
into items
|
2017-06-24 17:15:22 +02:00
|
|
|
finally do (quiet! (mapc #'load-file items)))
|
|
|
|
;; run all loaded tests
|
2017-12-10 15:37:18 -05:00
|
|
|
(if noninteractive
|
|
|
|
(ert-run-tests-batch-and-exit)
|
|
|
|
(call-interactively #'ert-run-tests-interactively)))
|
2017-06-24 17:15:22 +02:00
|
|
|
('error
|
|
|
|
(lwarn 'doom-test :error
|
|
|
|
"%s -> %s"
|
|
|
|
(car ex) (error-message-string ex)))))
|
2017-10-05 01:22:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
;; --- Test helpers -----------------------
|
|
|
|
|
|
|
|
(defmacro def-test! (name &rest body)
|
|
|
|
"Define a namespaced ERT test."
|
|
|
|
(declare (indent defun) (doc-string 2))
|
2017-12-31 11:45:02 -05:00
|
|
|
(let (plist)
|
|
|
|
(while (keywordp (car body))
|
|
|
|
(push (pop body) plist))
|
|
|
|
(setq plist (reverse plist))
|
|
|
|
(when (plist-get plist :skip)
|
|
|
|
(setq body `((ert-skip nil) ,@body)))
|
|
|
|
(when-let* ((modes (doom-enlist (plist-get plist :minor-mode))))
|
|
|
|
(dolist (mode modes)
|
|
|
|
(setq body `((with-minor-mode! ,mode ,@body)))))
|
|
|
|
(when-let* ((before (plist-get plist :before)))
|
|
|
|
(setq body `(,@before ,@body)))
|
|
|
|
(when-let* ((after (plist-get plist :after)))
|
|
|
|
(setq body `(,@body @after)))
|
2017-10-05 01:22:57 +02:00
|
|
|
`(ert-deftest
|
|
|
|
,(cl-loop with path = (file-relative-name (file-name-sans-extension load-file-name)
|
|
|
|
doom-emacs-dir)
|
|
|
|
for (rep . with) in '(("/test/" . "/") ("/" . ":"))
|
|
|
|
do (setq path (replace-regexp-in-string rep with path t t))
|
2017-12-31 11:45:02 -05:00
|
|
|
finally return (intern (format "%s::%s" path name)))
|
|
|
|
()
|
|
|
|
(with-temp-buffer
|
|
|
|
(save-mark-and-excursion
|
|
|
|
(save-window-excursion
|
|
|
|
,@body))))))
|
2017-10-05 01:22:57 +02:00
|
|
|
|
|
|
|
(defmacro should-buffer! (initial expected &rest body)
|
|
|
|
"Test that a buffer with INITIAL text, run BODY, then test it against EXPECTED.
|
|
|
|
|
|
|
|
INITIAL will recognize cursor markers in the form {[0-9]}. A {0} marker marks
|
|
|
|
where the cursor should be after setup. Otherwise, the cursor will be placed at
|
|
|
|
`point-min'.
|
|
|
|
|
|
|
|
EXPECTED will recognize one (optional) cursor marker: {|}, this is the
|
|
|
|
'expected' location of the cursor after BODY is finished, and will be tested
|
|
|
|
against."
|
|
|
|
(declare (indent 2))
|
|
|
|
`(with-temp-buffer
|
|
|
|
(cl-loop for line in ',initial
|
|
|
|
do (insert line "\n"))
|
|
|
|
(goto-char (point-min))
|
|
|
|
(let (marker-list)
|
|
|
|
(save-excursion
|
|
|
|
(while (re-search-forward "{\\([0-9]\\)}" nil t)
|
|
|
|
(push (cons (match-string 1)
|
|
|
|
(set-marker (make-marker) (match-beginning 0)))
|
|
|
|
marker-list)
|
|
|
|
(replace-match "" t t))
|
|
|
|
(if (not marker-list)
|
|
|
|
(goto-char (point-min))
|
|
|
|
(sort marker-list
|
|
|
|
(lambda (m1 m2) (< (marker-position m1)
|
|
|
|
(marker-position m2))))
|
|
|
|
(when (equal (caar marker-list) "0")
|
|
|
|
(goto-char! 0)))
|
|
|
|
,@body
|
|
|
|
(let ((result-text (buffer-substring-no-properties (point-min) (point-max)))
|
|
|
|
(point (point))
|
|
|
|
same-point
|
|
|
|
expected-text)
|
|
|
|
(with-temp-buffer
|
|
|
|
(cl-loop for line in ',expected
|
|
|
|
do (insert line "\n"))
|
|
|
|
(save-excursion
|
|
|
|
(goto-char 1)
|
|
|
|
(when (re-search-forward "{|}" nil t)
|
|
|
|
(setq same-point (= point (match-beginning 0)))
|
|
|
|
(replace-match "" t t)))
|
|
|
|
(setq expected-text (buffer-substring-no-properties (point-min) (point-max)))
|
|
|
|
(should (equal expected-text result-text))
|
|
|
|
(should same-point)))))))
|
|
|
|
|
|
|
|
(defmacro goto-char! (index)
|
|
|
|
"Meant to be used with `should-buffer!'. Will move the cursor to one of the
|
|
|
|
cursor markers. e.g. Go to marker {2} with (goto-char! 2)."
|
|
|
|
`(goto-char (point! ,index)))
|
|
|
|
|
|
|
|
(defmacro point! (index)
|
|
|
|
"Meant to be used with `should-buffer!'. Returns the position of a cursor
|
|
|
|
marker. e.g. {2} can be retrieved with (point! 2)."
|
|
|
|
`(cdr (assoc ,(cond ((numberp index) (number-to-string index))
|
|
|
|
((symbolp index) (symbol-name index))
|
|
|
|
((stringp index) index))
|
|
|
|
marker-list)))
|
2017-12-31 11:45:02 -05:00
|
|
|
|
|
|
|
(defmacro with-minor-mode! (mode &rest body)
|
|
|
|
"TODO"
|
|
|
|
(declare (indent defun))
|
|
|
|
`(progn (,mode +1)
|
|
|
|
,@body
|
|
|
|
(,mode -1)))
|