etest.el: Better output for etest-like comments.
[ETest.git] / etest-result-mode.el
blob42b50035e425745a59d52d3f4a45343e992cbc95
1 ;;; etest-result-mode.el --- Watch tests pass or fail
3 ;; Copyright (C) 2008 Philip Jackson
5 ;; Author: Philip Jackson <phil@shellarchive.co.uk>
7 ;; This file is not currently part of GNU Emacs.
9 ;; This program is free software; you can redistribute it and/or
10 ;; modify it under the terms of the GNU General Public License as
11 ;; published by the Free Software Foundation; either version 2, or (at
12 ;; your option) any later version.
14 ;; This program is distributed in the hope that it will be useful, but
15 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 ;; General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with this program ; see the file COPYING. If not, write to
21 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
24 ;;; Commentary:
26 ;; The default result mode for etest.
28 (require 'outline)
30 (declare-function etest-resultp "etest")
32 ;; calm down byte-compiler, you can have this one...
33 (eval-when-compile
34 (defvar current-results)
35 (require 'cl))
37 ;; The grouping of the status is for convenience and use by the
38 ;; font-locking so if you change the re don't forget to capture the
39 ;; status
40 (defvar etest-rm-not-ok-re "^ *\\(not ok\\) \\.\\."
41 "Regexp that will match bad test status.")
43 (defvar etest-rm-ok-re "^ *\\(ok\\) \\.\\."
44 "Regexp that will match good test status.")
46 (defvar etest-status-re
47 (concat "\\(" etest-rm-not-ok-re
48 "\\|" etest-rm-ok-re
49 "\\)")
50 "Regexp that will match a test status.")
52 (defvar etest-rm-map
53 (let ((m (make-keymap)))
54 (define-key m (kbd "q") 'bury-buffer)
55 (define-key m (kbd "g")
56 '(lambda ()
57 (interactive)
58 (etest-rm-refresh-buffer current-results)))
59 (define-key m (kbd "#") 'etest-rm-cycle-comments)
60 (define-key m (kbd "<tab>") 'etest-rm-toggle-headline)
61 m))
63 (defvar etest-rm-comment-visibility-types
64 '(show-all show-not-ok show-ok show-none))
66 (defvar etest-rm-comment-visibility-map
67 '((show-all etest-rm-show-all-comments)
68 (show-not-ok etest-rm-hide-ok-comments)
69 (show-ok etest-rm-hide-not-ok-comments)
70 (show-none etest-rm-hide-all-comments))
71 "Defines how the result buffer should look when the user is
72 toggling visibility states.")
74 (defgroup etest nil
75 "Emacs Testing Framework"
76 :group 'lisp)
78 (defun etest-rm-count-string-at-bol (string)
79 "Count how many instances of STRING are at the start of the
80 current line."
81 (save-excursion
82 (goto-char (point-at-bol))
83 (narrow-to-region (point) (point-at-eol))
84 (let ((count 0))
85 (while (looking-at string)
86 (forward-char)
87 (setq count (1+ count)))
88 (widen)
89 count)))
91 (defun etest-rm-outline-level ()
92 "Calculate what the current outline level should be. See
93 `ouline-level' for explination."
94 ;; we add one becuase there is an extra space before a status
95 (1+ (if (looking-at etest-status-re)
96 (etest-rm-count-string-at-bol " ")
97 (etest-rm-count-string-at-bol "*"))))
99 ;;;###autoload
100 (defun etest-result-mode (&optional results)
101 "Mode used to display test results."
102 (interactive)
103 (kill-all-local-variables)
104 (setq buffer-read-only t)
105 (outline-minor-mode)
106 (set (make-local-variable 'outline-regexp)
107 (concat "\\(\\*\\|" etest-status-re "\\)"))
108 (set (make-local-variable 'outline-level)
109 'etest-rm-outline-level)
110 (set (make-local-variable 'current-results) results)
111 (setq major-mode 'etest-result-mode)
112 (setq mode-name "etest-result")
113 (set (make-local-variable 'font-lock-defaults)
114 '(etest-rm-font-lock-keywords t t))
115 (use-local-map etest-rm-map)
116 (funcall (cadr (assoc (car etest-rm-comment-visibility-types)
117 etest-rm-comment-visibility-map))))
119 (defun etest-rm-pretty-print-status (result level)
120 "The pretty printing of a single test result. "
121 (let ((returned (plist-get result :result)))
122 (let* ((doc (plist-get result :doc))
123 (comments (plist-get result :comments))
124 (prefix (if returned "ok" "not ok")))
125 (insert (concat " " prefix " "))
126 (insert-char ?\. (- 18 (length prefix) level))
127 (insert ".. ")
128 (let ((col (current-column)))
129 (insert (concat doc "\n"))
130 (when comments
131 (mapc
132 (lambda (comment)
133 (indent-to col)
134 (insert (concat "# " comment "\n")))
135 (split-string comments "\n" t)))))))
137 (defun etest-rm-pretty-print-results (results &optional level)
138 "Pretty print the results of a run to a buffer. See also
139 `etest-rm-pretty-print-status'."
140 (let ((level (or level 0))
141 (res (car results)))
142 (cond
143 ((stringp res)
144 (insert-char ?\* (1+ level))
145 (insert (concat " " res "\n"))
146 (setq level (1+ level)))
147 ((etest-resultp res)
148 (indent-to level)
149 (etest-rm-pretty-print-status res level))
150 ((listp res)
151 (etest-rm-pretty-print-results res level)))
152 (when (cdr results)
153 (etest-rm-pretty-print-results (cdr results) level))))
155 (defun etest-rm-refresh-buffer (results)
156 "Refresh the results buffer using the cached test results."
157 (save-selected-window
158 (switch-to-buffer-other-window (get-buffer-create "*etest*"))
159 (setq buffer-read-only nil)
160 (erase-buffer)
161 (etest-rm-pretty-print-results results 0)
162 (etest-result-mode results)
163 (goto-char (point-min))
164 (when (search-forward-regexp etest-rm-not-ok-re nil t)
165 (goto-char (point-at-bol)))))
167 (defconst etest-rm-not-ok-face 'etest-rm-not-ok-face)
168 (defface etest-rm-not-ok-face
169 '((default (:inherit font-lock-warning-face)))
170 "Face used for failing tests."
171 :group 'etest)
173 (defconst etest-rm-ok-face 'etest-rm-ok-face)
174 (defface etest-rm-ok-face
175 '((default (:inherit font-lock-variable-name-face)))
176 "Face used for passing tests."
177 :group 'etest)
179 (defconst etest-rm-comment-face 'etest-rm-comment-face)
180 (defface etest-rm-comment-face
181 '((default (:inherit font-lock-comment-face)))
182 "Face used for comments."
183 :group 'etes)
185 (defconst etest-rm-heading-face 'etest-rm-heading-face)
186 (defface etest-rm-heading-face
187 '((default (:inherit font-lock-keyword-face)))
188 "Face used for headings."
189 :group 'etes)
191 (defconst etest-rm-font-lock-keywords
192 `((,etest-rm-ok-re 1 etest-rm-ok-face)
193 (,etest-rm-not-ok-re 1 etest-rm-not-ok-face)
194 ("^ *\\(#.+\\)" 1 etest-rm-comment-face)
195 ("^ *\\*+ \\(.+\\)" 1 etest-rm-heading-face)))
197 (defun etest-rm-toggle-headline ()
198 "Toggle the visibility of a test category."
199 (interactive)
200 (unless (looking-at outline-regexp)
201 (outline-previous-heading))
202 (if (get-char-property (point-at-eol) 'invisible)
203 (show-subtree)
204 (hide-subtree)))
206 ;;; comment toggling etc...
208 (defun etest-rm-cycle-comments ()
209 "Shift the values in `etest-rm-comment-visibility-types' and
210 use the `car' of that list to determine the visibility of
211 comments."
212 (interactive)
213 (setq etest-rm-comment-visibility-types
214 (concatenate 'list
215 (cdr etest-rm-comment-visibility-types)
216 (list (car etest-rm-comment-visibility-types))))
217 (etest-rm-refresh-buffer current-results)
218 (message (format "%S" (car etest-rm-comment-visibility-types))))
220 (defmacro etest-with-comments (&rest body)
221 "Eval BODY on each comment in the results buffer."
222 `(save-excursion
223 (goto-char (point-min))
224 (while (search-forward-regexp etest-status-re nil t)
225 (outline-previous-heading)
226 ,@body
227 (forward-line))))
229 (defun etest-rm-hide-not-ok-comments ()
230 "Hide all comments associated with a passing test in a result
231 buffer."
232 (interactive)
233 (etest-with-comments
234 (if (looking-at etest-rm-not-ok-re)
235 (hide-subtree)
236 (show-subtree))))
238 (defun etest-rm-hide-ok-comments ()
239 "Hide all comments associated with a passing test in a result
240 buffer."
241 (interactive)
242 (etest-with-comments
243 (if (looking-at etest-rm-ok-re)
244 (hide-subtree)
245 (show-subtree))))
247 (defun etest-rm-hide-all-comments ()
248 "Hide all comments in a result buffer."
249 (interactive)
250 (etest-with-comments
251 (hide-subtree)))
253 (defun etest-rm-show-all-comments ()
254 "Show all comments in a result buffer."
255 (interactive)
256 (etest-with-comments
257 (show-subtree)))
259 (provide 'etest-result-mode)