Reorganize unit-tests and test workflow
+ Moved unit tests out of tests/ and into their respective modules. + Rewrite makefile and added these tasks: + <MODULE>/<SUBMODULE> -- byte-compile a specific module + test:<MODULE>/<SUBMODULE> -- runs tests for a specific module + testi -- run tests in an interactive session of Emacs (WIP) + run -- opens an Emacs session with this config; useful when it is in a non-standard location.
This commit is contained in:
parent
cacd188286
commit
9c93c453e8
22 changed files with 511 additions and 423 deletions
|
@ -1,10 +1,15 @@
|
|||
;;; core/autoload/test.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defmacro def-test-group! (name &rest body)
|
||||
(declare (indent defun))
|
||||
(defmacro def-test! (name &rest body)
|
||||
"Define a namespaced ERT test."
|
||||
(declare (indent defun) (doc-string 2))
|
||||
(unless (plist-get body :disabled)
|
||||
(dolist (form body)
|
||||
(when (eq (car form) 'ert-deftest)
|
||||
(setf (cadr form) (intern (format "test-%s-%s" name (symbol-name (cadr form)))))))
|
||||
`(progn ,@body)))
|
||||
`(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))
|
||||
finally return (intern (format "%s::%s" path name))) ()
|
||||
()
|
||||
,@body)))
|
||||
|
|
|
@ -367,20 +367,16 @@ throw an error if the file doesn't exist."
|
|||
(defmacro require! (module submodule &optional reload-p)
|
||||
"Like `require', but for doom modules. Will load a module's config.el file if
|
||||
it hasn't already, and if it exists."
|
||||
(when (or (not noninteractive)
|
||||
(bound-and-true-p byte-compile-current-file)
|
||||
reload-p)
|
||||
(let ((loaded-p (doom-module-loaded-p module submodule)))
|
||||
(when (or reload-p (not loaded-p))
|
||||
(unless loaded-p
|
||||
(doom--enable-module module submodule t))
|
||||
`(condition-case-unless-debug ex
|
||||
(load! config ,(doom-module-path module submodule) t)
|
||||
('error
|
||||
(lwarn 'doom-modules :error
|
||||
"%s in '%s %s' -> %s"
|
||||
(car ex) ,module ',submodule (error-message-string ex))))))))
|
||||
|
||||
(let ((loaded-p (doom-module-loaded-p module submodule)))
|
||||
(when (or reload-p (not loaded-p))
|
||||
(unless loaded-p
|
||||
(doom--enable-module module submodule t))
|
||||
`(condition-case-unless-debug ex
|
||||
(load! config ,(doom-module-path module submodule) t)
|
||||
('error
|
||||
(lwarn 'doom-modules :error
|
||||
"%s in '%s %s' -> %s"
|
||||
(car ex) ,module ',submodule (error-message-string ex)))))))
|
||||
|
||||
(defmacro featurep! (module submodule)
|
||||
"Convenience macro that wraps `doom-module-loaded-p'."
|
||||
|
@ -532,7 +528,7 @@ If ONLY-RECOMPILE-P is non-nil, only recompile out-of-date files."
|
|||
(doom-initialize-packages t t)
|
||||
(let ((targets
|
||||
(cond ((equal (car command-line-args-left) "--")
|
||||
(cl-loop for file in command-line-args-left
|
||||
(cl-loop for file in (cdr command-line-args-left)
|
||||
if (file-exists-p file)
|
||||
collect (expand-file-name file)
|
||||
finally do (setq command-line-args-left nil)) )
|
||||
|
@ -604,6 +600,65 @@ package files."
|
|||
and do (message "Deleted %s" (file-relative-name path)))
|
||||
(message "Everything is clean"))))
|
||||
|
||||
(defun doom/run-tests (&optional modules)
|
||||
"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."
|
||||
(interactive) ;; TODO Add completing-read selection of tests
|
||||
;; FIXME Refactor this
|
||||
(condition-case-unless-debug ex
|
||||
(let (targets)
|
||||
;; ensure DOOM is initialized
|
||||
(let (noninteractive)
|
||||
(unload-feature 'core t)
|
||||
(load (expand-file-name "init.el" user-emacs-directory) nil t))
|
||||
(run-hooks 'emacs-startup-hook)
|
||||
;; collect targets
|
||||
(cond ((and command-line-args-left
|
||||
(equal (car command-line-args-left) "--"))
|
||||
(cl-loop for arg in (cdr argv)
|
||||
if (equal arg "core")
|
||||
do (push (expand-file-name "test/" doom-core-dir) targets)
|
||||
else
|
||||
collect
|
||||
(cl-destructuring-bind (car cdr) (split-string arg "/" t)
|
||||
(cons (intern (concat ":" car))
|
||||
(and (cadr consp) (intern cdr))))
|
||||
into args
|
||||
finally do (setq modules args
|
||||
command-line-args-left nil)))
|
||||
|
||||
(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
|
||||
(setq modules (doom--module-pairs)
|
||||
targets (list (expand-file-name "test/" doom-core-dir)))))
|
||||
;; resolve targets to a list of test files and load them
|
||||
(cl-loop with targets =
|
||||
(append targets
|
||||
(cl-loop for (module . submodule) in modules
|
||||
collect (doom-module-path module submodule "test/")))
|
||||
for dir in targets
|
||||
if (file-directory-p dir)
|
||||
nconc (reverse (directory-files-recursively dir "\\.el$"))
|
||||
into items
|
||||
finally do (quiet! (mapc #'load-file items)))
|
||||
;; run all loaded tests
|
||||
(when noninteractive
|
||||
(ert-run-tests-batch-and-exit)))
|
||||
('error
|
||||
(lwarn 'doom-test :error
|
||||
"%s -> %s"
|
||||
(car ex) (error-message-string ex)))))
|
||||
|
||||
|
||||
;;
|
||||
;; Package.el modifications
|
||||
|
|
|
@ -73,6 +73,7 @@ is enabled/disabled.'")
|
|||
("^\\*doom:" :regexp t :size 0.35 :noesc t :select t :modeline t)
|
||||
("^\\*doom " :regexp t :noselect t :autokill t :autoclose t)
|
||||
;; built-in (emacs)
|
||||
("*ert*" :same t :modeline t)
|
||||
("*info*" :size 0.5 :select t :autokill t)
|
||||
("*Backtrace*" :size 20 :noselect t)
|
||||
("*Warnings*" :size 8 :noselect t)
|
||||
|
|
99
core/test/autoload-buffers.el
Normal file
99
core/test/autoload-buffers.el
Normal file
|
@ -0,0 +1,99 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/test/autoload-buffers.el
|
||||
|
||||
(defmacro -with-temp-buffers! (buffer-args &rest body)
|
||||
(declare (indent defun))
|
||||
(let (buffers)
|
||||
(dolist (bsym buffer-args)
|
||||
(push `(,bsym (get-buffer-create ,(symbol-name bsym)))
|
||||
buffers))
|
||||
`(let* (,@buffers
|
||||
(buffer-list (list ,@(reverse (mapcar #'car buffers)))))
|
||||
,@body
|
||||
(mapc #'kill-buffer buffer-list))))
|
||||
|
||||
;;
|
||||
(def-test! get-buffers
|
||||
(-with-temp-buffers! (a b c)
|
||||
(should (cl-every #'buffer-live-p buffer-list))
|
||||
(should (equal buffer-list (list a b c)))
|
||||
(dolist (buf (list (cons a doom-emacs-dir)
|
||||
(cons b doom-emacs-dir)
|
||||
(cons c "/tmp/")))
|
||||
(with-current-buffer (car buf)
|
||||
(setq-local default-directory (cdr buf))))
|
||||
(with-current-buffer a
|
||||
;; should produce all buffers
|
||||
(let ((buffers (doom-buffer-list)))
|
||||
(should (cl-every (lambda (x) (memq x buffers)) (list a b c))))
|
||||
;; should produce only project buffers
|
||||
(let ((buffers (doom-buffer-list t)))
|
||||
(should (cl-every (lambda (x) (memq x buffers)) (list a b)))
|
||||
(should-not (memq c buffers))))
|
||||
;; If no project is available, just get all buffers
|
||||
(with-current-buffer c
|
||||
(let ((buffers (doom-buffer-list t)))
|
||||
(should (cl-every (lambda (x) (memq x buffers)) (list a b c)))))))
|
||||
|
||||
(def-test! get-real-buffers
|
||||
(-with-temp-buffers! (a b c d)
|
||||
(dolist (buf (list a b))
|
||||
(with-current-buffer buf
|
||||
(setq-local buffer-file-name "x")))
|
||||
(with-current-buffer c
|
||||
(rename-buffer "*C*"))
|
||||
(with-current-buffer d
|
||||
(doom-popup-mode +1))
|
||||
(let ((buffers (doom-real-buffers-list buffer-list)))
|
||||
(should (= (length buffers) 2))
|
||||
(should (cl-every (lambda (x) (memq x buffers)) (list a b)))
|
||||
(should (cl-notany (lambda (x) (memq x buffers)) (list c d))))))
|
||||
|
||||
(def-test! kill-buffers
|
||||
(-with-temp-buffers! (a b)
|
||||
(doom-kill-buffer a)
|
||||
(should-not (buffer-live-p a))
|
||||
;; modified buffer
|
||||
(with-current-buffer b
|
||||
(set-buffer-modified-p t))
|
||||
(doom-kill-buffer b t)
|
||||
(should-not (buffer-live-p a))))
|
||||
|
||||
(def-test! kill-buffer-then-show-real-buffer
|
||||
(-with-temp-buffers! (a b c d)
|
||||
(dolist (buf (list a b d))
|
||||
(with-current-buffer buf
|
||||
(setq-local buffer-file-name "x")))
|
||||
(should (cl-every #'buffer-live-p buffer-list))
|
||||
(switch-to-buffer a)
|
||||
(should (eq (current-buffer) a))
|
||||
(should (eq (selected-window) (get-buffer-window a)))
|
||||
(should (doom-kill-buffer a))
|
||||
(should (eq (current-buffer) b))
|
||||
(should (doom-kill-buffer))
|
||||
(should (eq (current-buffer) d))
|
||||
(doom/kill-this-buffer)
|
||||
(should (eq (current-buffer) (doom-fallback-buffer)))))
|
||||
|
||||
(def-test! matching-buffers
|
||||
(-with-temp-buffers! (a b c)
|
||||
(let ((buffers (doom-matching-buffers "^[ac]$")))
|
||||
(should (= 2 (length buffers)))
|
||||
(should (cl-every #'bufferp buffers))
|
||||
(should (cl-every (lambda (x) (memq x buffers)) (list a c)))
|
||||
(should (equal (reverse buffers)
|
||||
(doom-matching-buffers "^[ac]$" buffer-list))))))
|
||||
|
||||
(def-test! buffers-in-mode
|
||||
(-with-temp-buffers! (a b c d e)
|
||||
(dolist (buf (list a b))
|
||||
(with-current-buffer buf
|
||||
(emacs-lisp-mode)))
|
||||
(dolist (buf (list c d e))
|
||||
(with-current-buffer buf
|
||||
(text-mode)))
|
||||
(let ((el-buffers (doom-buffers-in-mode 'emacs-lisp-mode))
|
||||
(txt-buffers (doom-buffers-in-mode 'text-mode)))
|
||||
(should (cl-every #'buffer-live-p (append el-buffers txt-buffers)))
|
||||
(should (= 2 (length el-buffers)))
|
||||
(should (= 3 (length txt-buffers))))))
|
19
core/test/autoload-debug.el
Normal file
19
core/test/autoload-debug.el
Normal file
|
@ -0,0 +1,19 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/test/autoload-debug.el
|
||||
|
||||
(def-test! what-face
|
||||
(with-temp-buffer
|
||||
(insert (propertize "Hello " 'face 'font-lock-keyword-face))
|
||||
(insert "world")
|
||||
|
||||
(should (equal (doom/what-face (point-min)) '((font-lock-keyword-face) ())))
|
||||
(should-not (doom/what-face (point-max)))))
|
||||
|
||||
(def-test! what-face-overlays
|
||||
(with-temp-buffer
|
||||
(insert "Hello world")
|
||||
(let ((ov (make-overlay 1 6)))
|
||||
(overlay-put ov 'face 'font-lock-keyword-face))
|
||||
|
||||
(should (equal (doom/what-face (point-min)) '(() (font-lock-keyword-face))))
|
||||
(should-not (doom/what-face (point-max)))))
|
41
core/test/autoload-message.el
Normal file
41
core/test/autoload-message.el
Normal file
|
@ -0,0 +1,41 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/test/autoload-message.el
|
||||
|
||||
;; ansi messages
|
||||
(def-test! ansi-format
|
||||
(let ((noninteractive t))
|
||||
(should (equal (format! "Hello %s" "World")
|
||||
"Hello World"))
|
||||
(should (equal (format! (red "Hello %s" "World"))
|
||||
"[31mHello World[0m"))
|
||||
(should (equal (format! (green "Hello %s" "World"))
|
||||
(format "\e[%dm%s\e[0m"
|
||||
(cdr (assq 'green doom-message-fg))
|
||||
"Hello World")))
|
||||
(should (equal (format! (on-red "Hello %s" "World"))
|
||||
(format "\e[%dm%s\e[0m"
|
||||
(cdr (assq 'on-red doom-message-bg))
|
||||
"Hello World")))
|
||||
(should (equal (format! (bold "Hello %s" "World"))
|
||||
(format "\e[%dm%s\e[0m"
|
||||
(cdr (assq 'bold doom-message-fx))
|
||||
"Hello World")))))
|
||||
|
||||
(def-test! ansi-format-nested
|
||||
(let ((noninteractive t))
|
||||
(should (equal (format! (bold (red "Hello %s" "World")))
|
||||
(format "\e[%dm%s\e[0m" 1
|
||||
(format "\e[%dm%s\e[0m" 31 "Hello World"))))
|
||||
(should (equal (format! (on-red (bold "Hello %s" "World")))
|
||||
(format "\e[%dm%s\e[0m" 41
|
||||
(format "\e[%dm%s\e[0m" 1 "Hello World"))))
|
||||
(should (equal (format! (dark (white "Hello %s" "World")))
|
||||
(format "\e[%dm%s\e[0m" 2
|
||||
(format "\e[%dm%s\e[0m" 37 "Hello World"))))))
|
||||
|
||||
(def-test! ansi-format-apply
|
||||
(let ((noninteractive t))
|
||||
(should (equal (format! (color 'red "Hello %s" "World"))
|
||||
(format! (red "Hello %s" "World"))))
|
||||
(should (equal (format! (color (if nil 'red 'blue) "Hello %s" "World"))
|
||||
(format! (blue "Hello %s" "World"))))))
|
75
core/test/autoload-package.el
Normal file
75
core/test/autoload-package.el
Normal file
|
@ -0,0 +1,75 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/test/autoload-package.el
|
||||
|
||||
(defun -new-package (name version &optional reqs)
|
||||
(package-desc-create :name name :version version :reqs reqs))
|
||||
|
||||
(defmacro -with-temp-packages! (&rest forms)
|
||||
"Run FORMS in the context of a temporary package setup (as in, it won't
|
||||
affects your Emacs packages)."
|
||||
`(let* ((doom-local-dir ,(expand-file-name "test/.local/" doom-emacs-dir))
|
||||
(doom-packages-dir (concat doom-local-dir "packages/"))
|
||||
(doom-etc-dir (concat doom-local-dir "etc/"))
|
||||
(doom-cache-dir (concat doom-local-dir "cache/"))
|
||||
(package-user-dir (expand-file-name "elpa" doom-packages-dir))
|
||||
package-alist
|
||||
package-archive-contents
|
||||
package-initialize)
|
||||
(package-initialize)
|
||||
,@forms))
|
||||
|
||||
|
||||
;;
|
||||
;; Tests
|
||||
;;
|
||||
|
||||
(def-test! backend-detection
|
||||
(let ((package-alist `((doom-dummy ,(-new-package 'doom-dummy '(20160405 1234)))))
|
||||
(quelpa-cache '((doom-quelpa-dummy :fetcher github :repo "hlissner/does-not-exist")))
|
||||
(quelpa-initialized-p t))
|
||||
(should (eq (doom-package-backend 'doom-dummy) 'elpa))
|
||||
(should (eq (doom-package-backend 'doom-quelpa-dummy) 'quelpa))))
|
||||
|
||||
(def-test! elpa-outdated-detection
|
||||
(cl-letf (((symbol-function 'package-refresh-contents) (lambda (&rest _))))
|
||||
(let* ((doom--last-refresh (current-time))
|
||||
(package-alist
|
||||
`((doom-dummy ,(-new-package 'doom-dummy '(20160405 1234)))))
|
||||
(package-archive-contents
|
||||
`((doom-dummy ,(-new-package 'doom-dummy '(20170405 1234)))))
|
||||
(outdated (doom-package-outdated-p 'doom-dummy)))
|
||||
(should outdated)
|
||||
(should (equal outdated '(doom-dummy (20160405 1234) (20170405 1234)))))))
|
||||
|
||||
;; TODO quelpa-outdated-detection
|
||||
|
||||
(def-test! get-packages
|
||||
(let ((quelpa-initialized-p t)
|
||||
(doom-packages '((doom-dummy)))
|
||||
(package-alist
|
||||
`((doom-dummy nil)
|
||||
(doom-dummy-dep nil)))
|
||||
doom-core-packages)
|
||||
(cl-letf (((symbol-function 'doom-initialize-packages) (lambda (&rest _))))
|
||||
(should (equal (doom-get-packages) '((doom-dummy)))))))
|
||||
|
||||
(def-test! orphaned-packages
|
||||
"Test `doom-get-orphaned-packages', which gets a list of packages that are
|
||||
no longer enabled or depended on."
|
||||
(let ((doom-packages '((doom-dummy)))
|
||||
(package-alist
|
||||
`((doom-dummy ,(-new-package 'doom-dummy '(20160405 1234) '((doom-dummy-dep (1 0)))))
|
||||
(doom-dummy-unwanted ,(-new-package 'doom-dummy-unwanted '(20160601 1234)))
|
||||
(doom-dummy-dep ,(-new-package 'doom-dummy-dep '(20160301 1234)))))
|
||||
doom-core-packages)
|
||||
(cl-letf (((symbol-function 'doom-initialize-packages) (lambda (&rest _))))
|
||||
(should (equal (doom-get-orphaned-packages) '(doom-dummy-unwanted))))))
|
||||
|
||||
(def-test! missing-packages
|
||||
"Test `doom-get-missing-packages, which gets a list of enabled packages that
|
||||
aren't installed."
|
||||
(let ((doom-packages '((doom-dummy) (doom-dummy-installed)))
|
||||
(package-alist `((doom-dummy-installed ,(-new-package 'doom-dummy-installed '(20160405 1234)))))
|
||||
doom-core-packages)
|
||||
(cl-letf (((symbol-function 'doom-initialize-packages) (lambda (&rest _))))
|
||||
(should (equal (doom-get-missing-packages) '((doom-dummy)))))))
|
81
core/test/core-lib.el
Normal file
81
core/test/core-lib.el
Normal file
|
@ -0,0 +1,81 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/test/core-lib.el
|
||||
|
||||
;; `add-hook!'
|
||||
(def-test! add-one-to-one-hook
|
||||
(let (hooks)
|
||||
(add-hook! 'hooks 'a-hook)
|
||||
(should (equal hooks '(a-hook)))))
|
||||
|
||||
(def-test! add-many-to-one-hook
|
||||
(let (hooks)
|
||||
(add-hook! 'hooks '(hook-a hook-b hook-c))
|
||||
(should (equal hooks '(hook-c hook-b hook-a)))))
|
||||
|
||||
(def-test! add-one-to-many-hooks
|
||||
(let (hooks-a hooks-b hooks-c)
|
||||
(add-hook! '(hooks-a hooks-b hooks-c) 'a-hook)
|
||||
(should (equal hooks-a '(a-hook)))
|
||||
(should (equal hooks-b '(a-hook)))
|
||||
(should (equal hooks-c '(a-hook)))))
|
||||
|
||||
(def-test! add-many-to-many-hooks
|
||||
(let (hooks-a hooks-b hooks-c)
|
||||
(add-hook! '(hooks-a hooks-b hooks-c) '(hook-a hook-b hook-c))
|
||||
(should (equal hooks-a '(hook-c hook-b hook-a)))
|
||||
(should (equal hooks-b '(hook-c hook-b hook-a)))
|
||||
(should (equal hooks-c '(hook-c hook-b hook-a)))))
|
||||
|
||||
(def-test! add-non-literal-hooks
|
||||
(let (some-mode-hook)
|
||||
(add-hook! some-mode 'a-hook)
|
||||
(should (equal some-mode-hook '(a-hook)))))
|
||||
|
||||
;; `remove-hook!'
|
||||
(def-test! remove-hooks
|
||||
(let ((hooks-a '(hook-c hook-b hook-a))
|
||||
(hooks-b '(hook-c hook-b hook-a))
|
||||
(hooks-c '(hook-c hook-b hook-a)))
|
||||
(remove-hook! '(hooks-a hooks-b hooks-c) '(hook-a hook-b hook-c))
|
||||
(should (null hooks-a))
|
||||
(should (null hooks-b))
|
||||
(should (null hooks-c))))
|
||||
|
||||
(def-test! remove-hook-forms
|
||||
(let (hooks)
|
||||
(add-hook! 'hooks (message "Hello world"))
|
||||
(should hooks)
|
||||
(remove-hook! 'hooks (message "Hello world"))
|
||||
(should (null hooks))))
|
||||
|
||||
;; `add-transient-hook!'
|
||||
(def-test! transient-hooks
|
||||
(let (hooks value)
|
||||
(add-transient-hook! 'hooks (setq value t))
|
||||
(run-hooks 'hooks)
|
||||
(should (eq value t))
|
||||
(should (null hooks))))
|
||||
|
||||
(def-test! transient-function
|
||||
(let (value)
|
||||
(add-transient-hook! #'ignore (setq value (not value)))
|
||||
(ignore t)
|
||||
(should (eq value t))
|
||||
;; repeat to ensure it was only run once
|
||||
(ignore t)
|
||||
(should (eq value t))))
|
||||
|
||||
(def-test! unquote
|
||||
(should (equal (doom-unquote '(quote (a b c))) '(a b c)))
|
||||
;; nested
|
||||
(should (equal (doom-unquote '(quote (quote (a b c)))) '(a b c)))
|
||||
;; sub-quote
|
||||
(should (equal (doom-unquote '(quote (a (quote b) c))) '(a (quote b) c))))
|
||||
|
||||
(def-test! enlist
|
||||
(should (equal (doom-enlist 'a) '(a)))
|
||||
(should (equal (doom-enlist '(a)) '(a))))
|
||||
|
||||
|
||||
;; TODO `associate!'
|
||||
;; TODO `def-setting!' & `set!'
|
Loading…
Add table
Add a link
Reference in a new issue