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