* etest.el: whitespace.
[ETest.git] / etest-result-mode.el
blobc730b09e68b19ceff654684a932297b3d7a348ff
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 (eval-and-compile
31 (unless (fboundp 'declare-function)
32 (defmacro declare-function (&rest r))))
34 (declare-function etest-resultp "etest")
36 ;; calm down byte-compiler, you can have this one...
37 (eval-when-compile
38 (defvar current-results)
39 (defvar current-meta-info)
40 (require 'cl))
42 ;; The grouping of the status is for convenience and use by the
43 ;; font-locking so if you change the re don't forget to capture the
44 ;; status
45 (defvar etest-rm-not-ok-re "^ *\\(not ok\\) \\.\\."
46 "Regexp that will match bad test status.")
48 (defvar etest-rm-ok-re "^ *\\(ok\\) \\.\\."
49 "Regexp that will match good test status.")
51 (defvar etest-rm-todo-re "^ *\\(todo\\) \\.\\."
52 "Regexp that will match good test status.")
54 (defvar etest-status-re
55 (concat "\\(" etest-rm-not-ok-re
56 "\\|" etest-rm-ok-re
57 "\\|" etest-rm-todo-re
58 "\\)")
59 "Regexp that will match a test status.")
61 (defvar etest-meta-info-re
62 (concat "[[:blank:]]"
63 (regexp-opt '("total"
64 "pass"
65 "fail"
66 "started"
67 "finished") t)
68 "..\\{2,\\}[[:blank:]]+\\(.+\\)$")
69 "Regexp that will match the stats at the bottom of the buffer.")
71 (defvar etest-rm-map
72 (let ((m (make-keymap)))
73 (define-key m (kbd "q") 'bury-buffer)
74 (define-key m (kbd "#") 'etest-rm-cycle-comments)
75 (define-key m (kbd "TAB") 'etest-rm-toggle-headline)
76 (define-key m (kbd "<tab>") 'etest-rm-toggle-headline)
77 m))
79 (defvar etest-rm-comment-visibility-types
80 '(show-all show-not-ok show-ok show-none))
82 (defvar etest-rm-comment-visibility-map
83 '((show-all etest-rm-show-all-comments)
84 (show-not-ok etest-rm-hide-ok-comments)
85 (show-ok etest-rm-hide-not-ok-comments)
86 (show-none etest-rm-hide-all-comments))
87 "Defines how the result buffer should look when the user is
88 toggling visibility states.")
90 (defgroup etest nil
91 "Emacs Testing Framework"
92 :group 'lisp)
94 (defun etest-rm-count-string-at-bol (string)
95 "Count how many instances of STRING are at the start of the
96 current line."
97 (save-excursion
98 (goto-char (point-at-bol))
99 (narrow-to-region (point) (point-at-eol))
100 (let ((count 0))
101 (while (looking-at string)
102 (forward-char)
103 (setq count (1+ count)))
104 (widen)
105 count)))
107 (defun etest-rm-outline-level ()
108 "Calculate what the current outline level should be. See
109 `ouline-level' for explination."
110 ;; we add one becuase there is an extra space before a status
111 (1+ (if (looking-at etest-status-re)
112 (etest-rm-count-string-at-bol " ")
113 (etest-rm-count-string-at-bol "*"))))
115 ;;;###autoload
116 (defun etest-result-mode (&optional results meta-info)
117 "Mode used to display test results."
118 (interactive)
119 (kill-all-local-variables)
120 (setq buffer-read-only t)
121 (outline-minor-mode)
122 (set (make-local-variable 'outline-regexp)
123 (concat "\\(\\*\\|"
124 etest-status-re "\\|"
125 etest-meta-info-re "\\)"))
126 (set (make-local-variable 'outline-level)
127 'etest-rm-outline-level)
128 (set (make-local-variable 'current-results) results)
129 (set (make-local-variable 'current-meta-info) meta-info)
130 (setq major-mode 'etest-result-mode)
131 (setq mode-name "etest-result")
132 (set (make-local-variable 'font-lock-defaults)
133 '(etest-rm-font-lock-keywords t t))
134 (use-local-map etest-rm-map)
135 (funcall (cadr (assoc (car etest-rm-comment-visibility-types)
136 etest-rm-comment-visibility-map))))
138 (defun etest-rm-pretty-print-status (result level)
139 "The pretty printing of a single test result. "
140 (let ((returned (plist-get result :result)))
141 (let* ((doc (plist-get result :doc))
142 (comments (plist-get result :comments))
143 (prefix (if returned
144 (cond
145 ((plist-get result :todo) "todo")
146 (t "ok"))
147 "not ok")))
148 (insert (concat " " prefix " "))
149 (insert-char ?\. (- 18 (length prefix) level))
150 (insert ".. ")
151 (let ((col (current-column)))
152 (insert (concat doc "\n"))
153 (when comments
154 (mapc
155 (lambda (comment)
156 (indent-to col)
157 (insert (concat "# " comment "\n")))
158 (split-string comments "\n" t)))))))
160 (defun etest-rm-pretty-print-results (results &optional level)
161 "Pretty print the results of a run to a buffer. See also
162 `etest-rm-pretty-print-status'."
163 (let ((level (or level 0))
164 (res (car results)))
165 (cond
166 ((stringp res)
167 (insert-char ?\* (1+ level))
168 (insert (concat " " res "\n"))
169 (setq level (1+ level)))
170 ((etest-resultp res)
171 (indent-to level)
172 (etest-rm-pretty-print-status res level))
173 ((listp res)
174 (etest-rm-pretty-print-results res level)))
175 (when (cdr results)
176 (etest-rm-pretty-print-results (cdr results) level))))
178 (defun etest-rm-pretty-print-meta-info (meta-info)
179 "Insert a few details about the pass rate."
180 (let* ((pass (float (plist-get meta-info :pass)))
181 (fail (float (plist-get meta-info :fail)))
182 (total (+ pass fail))
183 (start (plist-get meta-info :timestart))
184 (finish (plist-get meta-info :timefinish)))
185 (insert (format (concat "\n total ..... %d\n"
186 " pass ...... %-3d (%3d%%)\n"
187 " fail ...... %-3d (%3d%%)")
188 total
189 pass
190 (* (/ pass total) 100)
191 fail
192 (* (/ fail total) 100)))
193 (insert (format (concat "\n started ... %s\n"
194 " finished .. %s (%f seconds)\n")
195 (current-time-string start)
196 (current-time-string finish)
197 (- (float-time finish) (float-time start))))))
200 (defun etest-rm-refresh-buffer (results &optional meta-info)
201 "Refresh the results buffer using the cached test results."
202 (save-selected-window
203 (switch-to-buffer-other-window (get-buffer-create "*etest*"))
204 (setq buffer-read-only nil)
205 (erase-buffer)
206 (etest-rm-pretty-print-results results 0)
207 (when meta-info
208 (etest-rm-pretty-print-meta-info meta-info))
209 (etest-result-mode results meta-info)
210 (goto-char (point-min))
211 (when (search-forward-regexp etest-rm-not-ok-re nil t)
212 (goto-char (point-at-bol)))))
214 (defconst etest-rm-not-ok-face 'etest-rm-not-ok-face)
215 (defface etest-rm-not-ok-face
216 '((default (:inherit font-lock-warning-face)))
217 "Face used for failing tests."
218 :group 'etest)
220 (defconst etest-rm-ok-face 'etest-rm-ok-face)
221 (defface etest-rm-ok-face
222 '((default (:inherit font-lock-variable-name-face)))
223 "Face used for passing tests."
224 :group 'etest)
226 (defconst etest-rm-comment-face 'etest-rm-comment-face)
227 (defface etest-rm-comment-face
228 '((default (:inherit font-lock-comment-face)))
229 "Face used for comments."
230 :group 'etes)
232 (defconst etest-rm-heading-face 'etest-rm-heading-face)
233 (defface etest-rm-heading-face
234 '((default (:inherit font-lock-keyword-face)))
235 "Face used for headings."
236 :group 'etes)
238 (defconst etest-rm-font-lock-keywords
239 `((,etest-rm-ok-re 1 etest-rm-ok-face)
240 (,etest-rm-not-ok-re 1 etest-rm-not-ok-face)
241 (,etest-rm-todo-re 1 etest-rm-not-ok-face)
242 (,etest-meta-info-re 1 etest-rm-heading-face)
243 ("^ *\\(#.+\\)" 1 etest-rm-comment-face)
244 ("^ *\\*+ \\(.+\\)" 1 etest-rm-heading-face)))
246 (defun etest-rm-toggle-headline ()
247 "Toggle the visibility of a test category."
248 (interactive)
249 (unless (looking-at outline-regexp)
250 (outline-previous-heading))
251 (if (get-char-property (point-at-eol) 'invisible)
252 (show-subtree)
253 (hide-subtree)))
255 ;;; comment toggling etc...
257 (defun etest-rm-cycle-comments ()
258 "Shift the values in `etest-rm-comment-visibility-types' and
259 use the `car' of that list to determine the visibility of
260 comments."
261 (interactive)
262 (setq etest-rm-comment-visibility-types
263 (concatenate 'list
264 (cdr etest-rm-comment-visibility-types)
265 (list (car etest-rm-comment-visibility-types))))
266 (etest-rm-refresh-buffer current-results current-meta-info)
267 (message (format "%S" (car etest-rm-comment-visibility-types))))
269 (defmacro etest-with-comments (&rest body)
270 "Eval BODY on each comment in the results buffer."
271 `(save-excursion
272 (goto-char (point-min))
273 (while (search-forward-regexp etest-status-re nil t)
274 (outline-previous-heading)
275 ,@body
276 (forward-line))))
278 (defun etest-rm-hide-not-ok-comments ()
279 "Hide all comments associated with a passing test in a result
280 buffer."
281 (interactive)
282 (etest-with-comments
283 (if (looking-at etest-rm-not-ok-re)
284 (hide-subtree)
285 (show-subtree))))
287 (defun etest-rm-hide-ok-comments ()
288 "Hide all comments associated with a passing test in a result
289 buffer."
290 (interactive)
291 (etest-with-comments
292 (if (looking-at etest-rm-ok-re)
293 (hide-subtree)
294 (show-subtree))))
296 (defun etest-rm-hide-all-comments ()
297 "Hide all comments in a result buffer."
298 (interactive)
299 (etest-with-comments
300 (hide-subtree)))
302 (defun etest-rm-show-all-comments ()
303 "Show all comments in a result buffer."
304 (interactive)
305 (etest-with-comments
306 (show-subtree)))
308 (provide 'etest-result-mode)