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:
Henrik Lissner 2017-06-14 20:26:17 +02:00
parent cacd188286
commit 9c93c453e8
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
22 changed files with 511 additions and 423 deletions

View file

@ -5,7 +5,7 @@ before_install:
- export PATH="/home/travis/.evm/bin:$PATH" - export PATH="/home/travis/.evm/bin:$PATH"
- evm config path /tmp - evm config path /tmp
- evm install $EVM_EMACS --use - evm install $EVM_EMACS --use
- cp test/init.test.el init.el - cp init.test.el init.el
- INSECURE=1 YES=1 make install - INSECURE=1 YES=1 make install
env: env:
- EVM_EMACS=emacs-25.1-travis - EVM_EMACS=emacs-25.1-travis

View file

@ -1,9 +1,9 @@
# Ensure emacs always runs from this makefile's PWD # Ensure emacs always runs from this makefile's PWD
EMACS_LIBS=-l core/core.el EMACS_FLAGS=--eval '(setq user-emacs-directory default-directory)' -l core/core.el
EMACS=emacs --batch --eval '(setq user-emacs-directory default-directory)' $(EMACS_LIBS) EMACS=emacs --batch $(EMACS_FLAGS)
TEST_EMACS=$(EMACS) --eval '(setq noninteractive nil)' $(EMACS_LIBS) EMACSI=emacs -q $(EMACS_FLAGS)
TESTS=$(shell find test/ -type f -name 'test-*.el')
MODULES=$(shell find modules/ -maxdepth 2 -type d) MODULES=$(patsubst modules/%, %, $(shell find modules/ -maxdepth 2 -type d))
# Tasks # Tasks
all: autoloads autoremove install update all: autoloads autoremove install update
@ -30,8 +30,8 @@ core: init.el clean
@$(EMACS) -f doom/compile -- init.el core @$(EMACS) -f doom/compile -- init.el core
$(MODULES): init.el .local/autoloads.el $(MODULES): init.el .local/autoloads.el
@rm -fv $(shell find $@ -maxdepth 2 -type f -name '*.elc') @rm -fv $(shell find modules/$@ -type f -name '*.elc')
@$(EMACS) -f doom/compile -- $@ @$(EMACS) -f doom/compile -- modules/$@
clean: clean:
@$(EMACS) -f doom/clean-compiled @$(EMACS) -f doom/clean-compiled
@ -43,10 +43,18 @@ clean-pcache:
@$(EMACS) -l persistent-soft --eval '(delete-directory pcache-directory t)' @$(EMACS) -l persistent-soft --eval '(delete-directory pcache-directory t)'
test: init.el .local/autoloads.el test: init.el .local/autoloads.el
@$(TEST_EMACS) $(patsubst %,-l %, $(TESTS)) -l test/run.el @$(EMACS) -f doom/run-tests
$(TESTS): init.el .local/autoloads.el test\:core $(patsubst %, test\:%, $(MODULES)): init.el .local/autoloads.el
@$(TEST_EMACS) $(patsubst %,-l %, $@) -l test/run.el @$(EMACS) -f doom/run-tests -- $(subst test:, , $@)
# run tests interactively
testi: init.el .local/autoloads.el
@DEBUG=1 $(EMACSI) -f doom/run-tests -f ert
# For running Emacs from a different folder than ~/.emacs.d
run:
@emacs $(EMACS_FLAGS) -l init.el
doctor: doctor:
@./bin/doctor @./bin/doctor
@ -62,4 +70,4 @@ init.el:
@$(EMACS) -f doom/compile -- $< @$(EMACS) -f doom/compile -- $<
.PHONY: all test $(TESTS) $(MODULES) .PHONY: all test $(MODULES)

View file

@ -1,10 +1,15 @@
;;; core/autoload/test.el -*- lexical-binding: t; -*- ;;; core/autoload/test.el -*- lexical-binding: t; -*-
;;;###autoload ;;;###autoload
(defmacro def-test-group! (name &rest body) (defmacro def-test! (name &rest body)
(declare (indent defun)) "Define a namespaced ERT test."
(declare (indent defun) (doc-string 2))
(unless (plist-get body :disabled) (unless (plist-get body :disabled)
(dolist (form body) `(ert-deftest
(when (eq (car form) 'ert-deftest) ,(cl-loop with path = (file-relative-name (file-name-sans-extension load-file-name)
(setf (cadr form) (intern (format "test-%s-%s" name (symbol-name (cadr form))))))) doom-emacs-dir)
`(progn ,@body))) 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)))

View file

@ -367,20 +367,16 @@ throw an error if the file doesn't exist."
(defmacro require! (module submodule &optional reload-p) (defmacro require! (module submodule &optional reload-p)
"Like `require', but for doom modules. Will load a module's config.el file if "Like `require', but for doom modules. Will load a module's config.el file if
it hasn't already, and if it exists." it hasn't already, and if it exists."
(when (or (not noninteractive) (let ((loaded-p (doom-module-loaded-p module submodule)))
(bound-and-true-p byte-compile-current-file) (when (or reload-p (not loaded-p))
reload-p) (unless loaded-p
(let ((loaded-p (doom-module-loaded-p module submodule))) (doom--enable-module module submodule t))
(when (or reload-p (not loaded-p)) `(condition-case-unless-debug ex
(unless loaded-p (load! config ,(doom-module-path module submodule) t)
(doom--enable-module module submodule t)) ('error
`(condition-case-unless-debug ex (lwarn 'doom-modules :error
(load! config ,(doom-module-path module submodule) t) "%s in '%s %s' -> %s"
('error (car ex) ,module ',submodule (error-message-string ex)))))))
(lwarn 'doom-modules :error
"%s in '%s %s' -> %s"
(car ex) ,module ',submodule (error-message-string ex))))))))
(defmacro featurep! (module submodule) (defmacro featurep! (module submodule)
"Convenience macro that wraps `doom-module-loaded-p'." "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) (doom-initialize-packages t t)
(let ((targets (let ((targets
(cond ((equal (car command-line-args-left) "--") (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) if (file-exists-p file)
collect (expand-file-name file) collect (expand-file-name file)
finally do (setq command-line-args-left nil)) ) finally do (setq command-line-args-left nil)) )
@ -604,6 +600,65 @@ package files."
and do (message "Deleted %s" (file-relative-name path))) and do (message "Deleted %s" (file-relative-name path)))
(message "Everything is clean")))) (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 ;; Package.el modifications

View file

@ -73,6 +73,7 @@ is enabled/disabled.'")
("^\\*doom:" :regexp t :size 0.35 :noesc t :select t :modeline t) ("^\\*doom:" :regexp t :size 0.35 :noesc t :select t :modeline t)
("^\\*doom " :regexp t :noselect t :autokill t :autoclose t) ("^\\*doom " :regexp t :noselect t :autokill t :autoclose t)
;; built-in (emacs) ;; built-in (emacs)
("*ert*" :same t :modeline t)
("*info*" :size 0.5 :select t :autokill t) ("*info*" :size 0.5 :select t :autokill t)
("*Backtrace*" :size 20 :noselect t) ("*Backtrace*" :size 20 :noselect t)
("*Warnings*" :size 8 :noselect t) ("*Warnings*" :size 8 :noselect t)

View 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))))))

View 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)))))

View 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"))
"Hello World"))
(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"))))))

View 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
View 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!'

View file

@ -1,4 +1,4 @@
;;; init.el ;;; init.test.el -- for automated unit tests -*- lexical-binding: t; -*-
(require 'core (concat user-emacs-directory "core/core")) (require 'core (concat user-emacs-directory "core/core"))

View file

@ -0,0 +1,46 @@
;; -*- no-byte-compile: t; -*-
;;; feature/evil/test/autoload-files.el
(defmacro -with-temp-files! (src dest &rest body)
"Run FORMS in the context of a temporary package setup (as in, it won't
affects your Emacs packages)."
(declare (indent 2) (doc-string 3))
`(let ((it ,src)
(other ,dest))
(with-temp-file it
(insert "Hello world"))
(unwind-protect
(progn
(should (file-exists-p it))
(find-file-literally it)
(should (equal (buffer-string) "Hello world"))
(should (equal (buffer-file-name) it))
(let ((inhibit-message (not doom-debug-mode)))
,@body))
(ignore-errors (delete-file it))
,(if dest `(ignore-errors (delete-file other))))))
;;
(def-test! move-this-file
":mv"
(-with-temp-files! "/tmp/doom-buffer" "/tmp/doom-buffer-new"
(should-error (+evil:move-this-file it))
(should (+evil:move-this-file other t))
(should (file-exists-p other))
(should (not (file-exists-p it)))))
(def-test! copy-this-file
":cp"
(-with-temp-files! "/tmp/doom-buffer-2" "/tmp/doom-buffer-2-new"
(should-error (+evil:copy-this-file it))
(should (+evil:copy-this-file other t))
(should (file-exists-p other))
(should (file-exists-p it))))
(def-test! delete-this-file
":rm"
(-with-temp-files! "/tmp/doom-buffer-3" nil
(should-error (+evil:delete-this-file "this-file-does-not-exist"))
(should (+evil:delete-this-file nil t))
(should (not (file-exists-p it)))))

View file

@ -1,30 +1,9 @@
;;; test/modules/feature/test-evil.el ;; -*- no-byte-compile: t; -*-
;;; feature/evil/test/evil.el
(require! :feature evil t) ;; `+evil*ex-replace-special-filenames'
(def-test! file-modifiers
(defalias 'do-it '+evil*ex-replace-special-filenames) (cl-flet ((do-it #'+evil*ex-replace-special-filenames))
(defmacro do-files! (src dest &rest body)
(declare (indent defun))
`(let ((it ,src)
(other ,dest))
(with-temp-file it
(insert "Hello world"))
(unwind-protect
(progn
(should (file-exists-p it))
(find-file-literally it)
(should (equal (buffer-string) "Hello world"))
(should (equal (buffer-file-name) it))
(let ((inhibit-message (not doom-debug-mode)))
,@body))
(ignore-errors (delete-file it))
,(if dest `(ignore-errors (delete-file other))))))
;;
(def-test-group! feature/evil
(ert-deftest custom-file-modifiers ()
(let ((buffer-file-name "~/.emacs.d/test/modules/feature/test-evil.el") (let ((buffer-file-name "~/.emacs.d/test/modules/feature/test-evil.el")
(default-directory "~/.emacs.d/test/modules/")) (default-directory "~/.emacs.d/test/modules/"))
(should (equal (do-it "%") "feature/test-evil.el")) (should (equal (do-it "%") "feature/test-evil.el"))
@ -39,9 +18,10 @@
(should (equal (do-it "%:s?e?x?") "fxature/test-evil.el")) (should (equal (do-it "%:s?e?x?") "fxature/test-evil.el"))
(should (equal (do-it "%:gs?e?x?") "fxaturx/txst-xvil.xl")) (should (equal (do-it "%:gs?e?x?") "fxaturx/txst-xvil.xl"))
(should (equal (file-truename (do-it "%:p")) (should (equal (file-truename (do-it "%:p"))
(file-truename buffer-file-name))))) (file-truename buffer-file-name))))))
(ert-deftest custom-nested-file-modifiers () (def-test! nested-file-modifiers
(cl-flet ((do-it #'+evil*ex-replace-special-filenames))
(let ((buffer-file-name "~/vim/src/version.c") (let ((buffer-file-name "~/vim/src/version.c")
(default-directory "~/vim/")) (default-directory "~/vim/"))
(should (equal (do-it "%:p") (expand-file-name "~/vim/src/version.c"))) (should (equal (do-it "%:p") (expand-file-name "~/vim/src/version.c")))
@ -54,9 +34,10 @@
(should (equal (do-it "%:p:t") "version.c")) (should (equal (do-it "%:p:t") "version.c"))
(should (equal (do-it "%:r") "src/version")) (should (equal (do-it "%:r") "src/version"))
(should (equal (do-it "%:p:r") (expand-file-name "~/vim/src/version"))) (should (equal (do-it "%:p:r") (expand-file-name "~/vim/src/version")))
(should (equal (do-it "%:t:r") "version")))) (should (equal (do-it "%:t:r") "version")))))
(ert-deftest custom-empty-file-modifiers () (def-test! empty-file-modifiers
(cl-flet ((do-it #'+evil*ex-replace-special-filenames))
(let (buffer-file-name default-directory) (let (buffer-file-name default-directory)
(should (equal (do-it "%") "")) (should (equal (do-it "%") ""))
(should (equal (do-it "%:r") "")) (should (equal (do-it "%:r") ""))
@ -67,24 +48,5 @@
(should (equal (do-it "%:~") "")) (should (equal (do-it "%:~") ""))
(should (equal (do-it "%:s?e?x?") "")) (should (equal (do-it "%:s?e?x?") ""))
(should (equal (do-it "%:gs?e?x?") "")) (should (equal (do-it "%:gs?e?x?") ""))
(should (equal (do-it "%:P") "")))) (should (equal (do-it "%:P") "")))))
(ert-deftest move-this-file ()
(do-files! "/tmp/doom-buffer" "/tmp/doom-buffer-new"
(should-error (+evil:move-this-file it))
(should (+evil:move-this-file other t))
(should (file-exists-p other))
(should (not (file-exists-p it)))))
(ert-deftest copy-this-file ()
(do-files! "/tmp/doom-buffer-2" "/tmp/doom-buffer-2-new"
(should-error (+evil:copy-this-file it))
(should (+evil:copy-this-file other t))
(should (file-exists-p other))
(should (file-exists-p it))))
(ert-deftest delete-this-file ()
(do-files! "/tmp/doom-buffer-3" nil
(should-error (+evil:delete-this-file "this-file-does-not-exist"))
(should (+evil:delete-this-file nil t))
(should (not (file-exists-p it))))))

View file

@ -0,0 +1,35 @@
;; -*- no-byte-compile: t; -*-
;;; tools/password-store/test/autoload-pass.el
(defmacro -with-passwords! (buffer-args &rest body)
(declare (indent defun))
`(cl-letf
(((symbol-function 'auth-pass-parse-entry)
(lambda (entry)
(when (equal entry "fake/source")
'((secret . "defuse-account-gad")
("login" . "HL2532-GANDI")
("alt-login" . "hlissner")
("email" . "henrik@lissner.net")
("url" . "https://www.gandi.net/login"))))))
,@body))
;;
(def-test! get-field
(-with-passwords!
(should (equal (+pass-get-field "fake/source" "login")
"HL2532-GANDI"))
(should (equal (+pass-get-field "fake/source" "email")
"henrik@lissner.net"))
(should (equal (+pass-get-field "fake/source" '("alt-login" "email"))
"hlissner"))
(should (equal (+pass-get-field "fake/source" '("username" "email"))
"henrik@lissner.net"))
(should-not (+pass-get-field "fake/source" '("x" "y" "z")))
(should-error (+pass-get-field "nonexistent/source" "login"))))
(def-test! get-login
(-with-passwords!
(should (equal (+pass-get-user "fake/source") "HL2532-GANDI"))
(should (equal (+pass-get-secret "fake/source") "defuse-account-gad"))))

View file

@ -1,95 +0,0 @@
;;; test/core/autoload/test-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-group! core/autoload/buffers
(ert-deftest 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)))))))
(ert-deftest 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))))))
(ert-deftest 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))))
(ert-deftest 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)))))
(ert-deftest 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))))))
(ert-deftest 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)))))))

View file

@ -1,19 +0,0 @@
;;; test/core/autoload/test-debug.el
(def-test-group! core/autoload/debug
(ert-deftest 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)))))
(ert-deftest 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))))))

View file

@ -1,41 +0,0 @@
;;; test/core/autoload/test-message.el
(def-test-group! core/autoload/message
;; ansi messages
(ert-deftest ansi-format ()
(let ((noninteractive t))
(should (equal (format! "Hello %s" "World")
"Hello World"))
(should (equal (format! (red "Hello %s" "World"))
"Hello World"))
(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")))))
(ert-deftest 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"))))))
(ert-deftest 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")))))))

View file

@ -1,79 +0,0 @@
;;; test/core/autoload/test-package.el
(defun test-package-new (name version &optional reqs)
(package-desc-create :name name :version version :reqs reqs))
;; TODO
(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-group! core/autoload/package
(ert-deftest backend-detection ()
"TODO"
(let ((package-alist `((doom-dummy ,(test-package-new '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))))
(ert-deftest elpa-outdated-detection ()
"TODO"
(cl-letf (((symbol-function 'package-refresh-contents) (lambda (&rest _))))
(let* ((doom--last-refresh (current-time))
(package-alist
`((doom-dummy ,(test-package-new 'doom-dummy '(20160405 1234)))))
(package-archive-contents
`((doom-dummy ,(test-package-new '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
(ert-deftest get-packages ()
"TODO"
(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)))))))
(ert-deftest 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 ,(test-package-new 'doom-dummy '(20160405 1234) '((doom-dummy-dep (1 0)))))
(doom-dummy-unwanted ,(test-package-new 'doom-dummy-unwanted '(20160601 1234)))
(doom-dummy-dep ,(test-package-new '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))))))
(ert-deftest 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 ,(test-package-new '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))))))))

View file

@ -1,72 +0,0 @@
;;; ../test/test-core-lib.el
(def-test-group! core-lib
;; `add-hook!'
(ert-deftest add-one-to-one-hook ()
(let (hooks)
(add-hook! 'hooks 'a-hook)
(should (equal hooks '(a-hook)))))
(ert-deftest 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)))))
(ert-deftest 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)))))
(ert-deftest 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)))))
(ert-deftest add-non-literal-hooks ()
(let (some-mode-hook)
(add-hook! some-mode 'a-hook)
(should (equal some-mode-hook '(a-hook)))))
;; `remove-hook!'
(ert-deftest 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))))
(ert-deftest 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!'
(ert-deftest transient-hooks ()
(let (hooks value)
(add-transient-hook! 'hooks (setq value t))
(run-hooks 'hooks)
(should (eq value t))
(should (null hooks))))
(ert-deftest 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))))
;; TODO `associate!'
;; TODO `def-setting!' & `set!'
)

View file

@ -1,23 +0,0 @@
;;; test/modules/feature/test-password-store.el
(require! :tools password-store t)
;;
(def-test-group! tools/password-store
(ert-deftest get-field ()
(let ((data `((secret . "defuse-account-gad")
("login" . "HL2532-GANDI")
("alt-login" . "hlissner")
("email" . "henrik@lissner.net")
("url" . "https://www.gandi.net/login"))))
(should (equal (+pass-get-field data "login")
"HL2532-GANDI"))
(should (equal (+pass-get-field data "email")
"henrik@lissner.net"))
(should (equal (+pass-get-field data '("alt-login" "email"))
"hlissner"))
(should (equal (+pass-get-field data '("username" "email"))
"henrik@lissner.net"))
(should-not (+pass-get-field data '("x" "y" "z"))))
(should-error (+pass-get-field nil nil))))

View file

@ -1,6 +0,0 @@
;;; ../test/setup.el
(setq-default debug-on-error nil)
(run-hooks 'emacs-startup-hook)
(ert-run-tests-batch-and-exit)

View file

@ -1,4 +0,0 @@
;;; ../test/test-basic.el
(ert-deftest doom-basic ()
(should t))