Download
(defvar unit-test-command nil
"A function that runs the unit tests for this project.
This should have no required arguments and return nil if tests failed,
`handled' if the test indicator will be updated later, or non-nil otherwise.
Examples:
(setq unit-test-command 'my-defun)
(setq unit-test-command (lambda () ...))
(setq unit-test-command (lambda (&optional arg)
(interactive \"P\")
...))
")
(make-variable-buffer-local 'unit-test-command)
(defvar unit-test-file-fn nil
"A function that takes an absolute path to a file and returns an absolute
path to the corresponding file of unit tests, or nil if this file has no
unit tests.")
(make-variable-buffer-local 'unit-test-file-fn)
(defvar unit-test-colours '(("orange" . "#FF9900")
("dark-orange" . "#E86400")
("green" . "#00FF00")
("dark-green" . "#00C400")
("red" . "#FF0000")
("dark-red" . "#C40000")))
(defun unit-test-dot (colour)
"Return an XPM string representing a dot whose colour is COLOUR."
(format "/* XPM */
static char * test_pass_xpm[] = {
\"18 13 4 1\",
\" c None\",
\". c #000000\",
\"+ c %s\",
\"c c %s\",
\" \",
\" ..... \",
\" .ccccc. \",
\" .cc+++cc. \",
\" .cc+++++cc. \",
\" .c+++++++c. \",
\" .c+++++++c. \",
\" .c+++++++c. \",
\" .cc+++++cc. \",
\" .cc+++cc. \",
\" .ccccc. \",
\" ..... \",
\" \"};"
(cdr (assoc colour unit-test-colours))
(cdr (assoc (concat "dark-"colour) unit-test-colours))))
(defvar unit-test-passed-xpm (unit-test-dot "green")
"An XPM image displayed in the mode-line when all unit tests pass.")
(defvar unit-test-failed-xpm (unit-test-dot "red")
"An XPM image displayed in the mode-line when some unit tests fail.")
(defvar unit-test-running-xpm (unit-test-dot "orange")
"An XPM image displayed in the mode-line while tests are running.")
(defvar unit-test-passed-string ":o)"
"A string displayed in the mode-line when all unit tests pass.")
(defvar unit-test-failed-string ":o("
"A string displayed in the mode-line when some unit tests fail.")
(defvar unit-test-running-string ":o0"
"A string displayed in the mode-line while tests are running.")
(defvar unit-tests-passed-hook '())
(defvar unit-tests-failed-hook '())
(defun show-test-status (status)
(with-current-buffer (or last-unit-test-buffer
(current-buffer))
(let ((map (make-sparse-keymap)))
(define-key map [mode-line mouse-1] 'show-test-none)
(setq mode-line-buffer-identification
(if (and window-system
(member 'xpm image-types))
`(,(propertize " %b"
'help-echo (case status
(passed
"Tests passed")
(failed
"Some tests failed")
(running
"Tests running"))
'keymap map
'display
`(image :type xpm
:data ,(case status
(passed unit-test-passed-xpm)
(failed unit-test-failed-xpm)
(running
unit-test-running-xpm))
:ascent center)))
`(,(format " [%s] %%b"
(case status
(passed unit-test-passed-string)
(failed unit-test-failed-string)
(running unit-test-running-string))))))
(ignore-errors
(force-mode-line-update)
(redraw-modeline)))))
(defun show-test-none ()
(interactive)
(setq mode-line-buffer-identification '(#("%12b ")))
(when (fboundp 'redraw-modeline) (redraw-modeline)))
(defvar last-unit-test-buffer nil)
(defun run-unit-tests ()
(interactive)
(unless unit-test-command
(set-unit-test-command))
(setq last-unit-test-buffer (current-buffer))
(show-test-status 'running)
(sit-for 0)
(let ((result (if (commandp unit-test-command)
(call-interactively unit-test-command)
(funcall unit-test-command))))
(cond ((eq result 'handled) nil)
(result
(run-hooks 'unit-tests-passed-hook)
(show-test-status 'passed))
(t (run-hooks 'unit-tests-failed-hook)
(show-test-status 'failed)))))
(defun set-unit-test-command ()
(interactive)
(setq unit-test-command
(read-from-minibuffer "Function to run unit tests: "
(format "%S" unit-test-command)
read-expression-map t
'read-expression-history)))
(defun open-unit-test-file ()
"Open the file of unit tests for the current buffer"
(interactive)
(if (and (boundp 'unit-test-window-configuration))
(set-window-configuration unit-test-window-configuration)
(let ((window-configuration (current-window-configuration))
(file (buffer-file-name (current-buffer))))
(if (and file unit-test-file-fn)
(let ((unit-tests (funcall unit-test-file-fn file)))
(when unit-tests
(pop-to-buffer (or (find-buffer-visiting unit-tests)
(find-file-noselect unit-tests)))
(set (make-local-variable 'unit-test-window-configuration)
window-configuration)))
(message "No unit test file known for this buffer.")))))
(provide 'unit-test)