1 ;;; flymake-tests.el --- Test suite for flymake -*- lexical-binding: t -*-
3 ;; Copyright (C) 2011-2018 Free Software Foundation, Inc.
5 ;; Author: Eduard Wiebe <usenet@pusto.de>
7 ;; This file is part of GNU Emacs.
9 ;; GNU Emacs is free software: you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
27 (eval-when-compile (require 'subr-x
)) ; string-trim
29 (defvar flymake-tests-data-directory
30 (expand-file-name "lisp/progmodes/flymake-resources"
31 (or (getenv "EMACS_TEST_DIRECTORY")
32 (expand-file-name "../../.."
35 "Directory containing flymake test data.")
40 (defun flymake-tests--wait-for-backends ()
41 ;; Weirdness here... https://debbugs.gnu.org/17647#25
42 ;; ... meaning `sleep-for', and even
43 ;; `accept-process-output', won't suffice as ways to get
44 ;; process filters and sentinels to run, though they do work
45 ;; fine in a non-interactive batch session. The only thing
46 ;; that will indeed unblock pending process output is
47 ;; reading an input event, so, as a workaround, use a dummy
48 ;; `read-event' with a very short timeout.
49 (unless noninteractive
(read-event "" nil
0.1))
51 for notdone
= (cl-set-difference (flymake-running-backends)
52 (flymake-reporting-backends))
54 unless noninteractive do
(read-event "" nil
0.1)
55 do
(sleep-for (+ 0.5 flymake-no-changes-timeout
))
56 finally
(when notdone
(ert-fail
57 (format "Some backends not reporting yet %s"
60 (cl-defun flymake-tests--call-with-fixture (fn file
61 &key
(severity-predicate
62 nil sev-pred-supplied-p
))
63 "Call FN after flymake setup in FILE, using `flymake-proc`.
64 SEVERITY-PREDICATE is used to setup
65 `flymake-proc-diagnostic-type-pred'"
66 (let* ((file (expand-file-name file flymake-tests-data-directory
))
67 (visiting (find-buffer-visiting file
))
68 (buffer (or visiting
(find-file-noselect file
)))
69 (process-environment (cons "LC_ALL=C" process-environment
))
70 (warning-minimum-log-level :error
))
72 (with-current-buffer buffer
74 (when sev-pred-supplied-p
75 (setq-local flymake-proc-diagnostic-type-pred severity-predicate
))
76 (goto-char (point-min))
77 (let ((flymake-start-on-flymake-mode nil
))
78 (unless flymake-mode
(flymake-mode 1)))
80 (flymake-tests--wait-for-backends)
84 (let (kill-buffer-query-functions) (kill-buffer buffer
))))))
86 (cl-defmacro flymake-tests--with-flymake
((file &rest args
)
89 (debug (sexp &rest form
)))
90 `(flymake-tests--call-with-fixture (lambda () ,@body
) ,file
,@args
))
92 (ert-deftest warning-predicate-rx-gcc
()
93 "Test GCC warning via regexp predicate."
94 (skip-unless (and (executable-find "gcc") (executable-find "make")))
95 (flymake-tests--with-flymake
96 ("test.c" :severity-predicate
"^[Ww]arning")
97 (flymake-goto-next-error)
98 (should (eq 'flymake-warning
101 (ert-deftest warning-predicate-function-gcc
()
102 "Test GCC warning via function predicate."
103 (skip-unless (and (executable-find "gcc") (executable-find "make")))
104 (flymake-tests--with-flymake
105 ("test.c" :severity-predicate
106 (lambda (msg) (string-match "^[Ww]arning" msg
)))
107 (flymake-goto-next-error)
108 (should (eq 'flymake-warning
111 (ert-deftest perl-backend
()
112 "Test the perl backend"
113 (skip-unless (executable-find "perl"))
114 (flymake-tests--with-flymake ("test.pl")
115 (flymake-goto-next-error)
116 (should (eq 'flymake-warning
(face-at-point)))
117 (goto-char (point-max))
118 (flymake-goto-prev-error)
119 (should (eq 'flymake-error
(face-at-point)))))
121 (ert-deftest ruby-backend
()
122 "Test the ruby backend"
123 (skip-unless (executable-find "ruby"))
124 ;; Some versions of ruby fail if HOME doesn't exist (bug#29187).
125 (let* ((tempdir (make-temp-file "flymake-tests-ruby" t
))
126 (process-environment (cons (format "HOME=%s" tempdir
)
127 process-environment
))
128 ;; And see https://debbugs.gnu.org/cgi/bugreport.cgi?bug=19657#20
129 ;; for this particular yuckiness
130 (abbreviated-home-dir nil
))
132 (flymake-tests--with-flymake ("test.rb")
133 (flymake-goto-next-error)
134 (should (eq 'flymake-warning
(face-at-point)))
135 (flymake-goto-next-error)
136 (should (eq 'flymake-error
(face-at-point))))
137 (delete-directory tempdir t
))))
139 (ert-deftest different-diagnostic-types
()
140 "Test GCC warning via function predicate."
141 (skip-unless (and (executable-find "gcc")
144 (shell-command-to-string "gcc -dumpversion")))
145 (executable-find "make")))
146 (let ((flymake-wrap-around nil
))
147 (flymake-tests--with-flymake
148 ("errors-and-warnings.c")
149 (flymake-goto-next-error)
150 (should (eq 'flymake-error
(face-at-point)))
151 (flymake-goto-next-error)
152 (should (eq 'flymake-note
(face-at-point)))
153 (flymake-goto-next-error)
154 (should (eq 'flymake-warning
(face-at-point)))
155 (flymake-goto-next-error)
156 (should (eq 'flymake-error
(face-at-point)))
157 (flymake-goto-next-error)
158 (should (eq 'flymake-warning
(face-at-point)))
159 (flymake-goto-next-error)
160 (should (eq 'flymake-warning
(face-at-point)))
161 (should-error (flymake-goto-next-error nil nil t
)))))
163 (ert-deftest included-c-header-files
()
164 "Test inclusion of .h header files."
165 (skip-unless (and (executable-find "gcc") (executable-find "make")))
166 (let ((flymake-wrap-around nil
))
167 (flymake-tests--with-flymake
169 (flymake-goto-next-error)
170 (should (eq 'flymake-warning
(face-at-point)))
171 (flymake-goto-next-error)
172 (should (eq 'flymake-error
(face-at-point)))
173 (should-error (flymake-goto-next-error nil nil t
)))
174 (flymake-tests--with-flymake
176 (should-error (flymake-goto-next-error nil nil t
)))))
178 (defmacro flymake-tests--assert-set
(set
185 collect
`(should (memq (quote ,s
) ,set
)))
188 collect
`(should-not (memq (quote ,s
) ,set
)))))
190 (defun flymake-tests--diagnose-words
191 (report-fn type words
)
192 "Helper. Call REPORT-FN with diagnostics for WORDS in buffer."
198 (goto-char (point-min))
199 (cl-loop while
(word-search-forward word nil t
)
200 collect
(flymake-make-diagnostic
205 (concat word
" is wrong")))))))
207 (ert-deftest dummy-backends
()
208 "Test many different kinds of backends."
211 (((symbol-function 'error-backend
)
215 #'flymake-tests--diagnose-words report-fn
:error
'("manha" "prognata"))))
216 ((symbol-function 'warning-backend
)
220 #'flymake-tests--diagnose-words report-fn
:warning
'("ut" "dolor"))))
221 ((symbol-function 'sync-backend
)
223 (flymake-tests--diagnose-words report-fn
:note
'("quis" "commodo"))))
224 ((symbol-function 'panicking-backend
)
228 report-fn
:panic
:explanation
"The spanish inquisition!")))
229 ((symbol-function 'crashing-backend
)
231 ;; HACK: Shoosh log during tests
232 (setq-local warning-minimum-log-level
:emergency
)
234 (insert "Lorem ipsum dolor sit amet, consectetur adipiscing
235 elit, sed do eiusmod tempor incididunt ut labore et dolore
236 manha aliqua. Ut enim ad minim veniam, quis nostrud
237 exercitation ullamco laboris nisi ut aliquip ex ea commodo
238 consequat. Duis aute irure dolor in reprehenderit in
239 voluptate velit esse cillum dolore eu fugiat nulla
240 pariatur. Excepteur sint occaecat cupidatat non prognata
241 sunt in culpa qui officia deserunt mollit anim id est
243 (let ((flymake-diagnostic-functions
244 (list 'error-backend
'warning-backend
'sync-backend
248 (flymake-wrap-around nil
))
249 (let ((flymake-start-on-flymake-mode nil
))
253 (flymake-tests--assert-set (flymake-running-backends)
254 (error-backend warning-backend panicking-backend
)
257 (flymake-tests--assert-set (flymake-disabled-backends)
259 (error-backend warning-backend sync-backend
262 (flymake-tests--wait-for-backends)
264 (flymake-tests--assert-set (flymake-disabled-backends)
265 (crashing-backend panicking-backend
)
266 (error-backend warning-backend sync-backend
))
268 (goto-char (point-min))
269 (flymake-goto-next-error)
270 (should (eq 'flymake-warning
(face-at-point))) ; dolor
271 (flymake-goto-next-error)
272 (should (eq 'flymake-warning
(face-at-point))) ; ut
273 (flymake-goto-next-error)
274 (should (eq 'flymake-error
(face-at-point))) ; manha
275 (flymake-goto-next-error)
276 (should (eq 'flymake-warning
(face-at-point))) ; Ut
277 (flymake-goto-next-error)
278 (should (eq 'flymake-note
(face-at-point))) ; quis
279 (flymake-goto-next-error)
280 (should (eq 'flymake-warning
(face-at-point))) ; ut
281 (flymake-goto-next-error)
282 (should (eq 'flymake-note
(face-at-point))) ; commodo
283 (flymake-goto-next-error)
284 (should (eq 'flymake-warning
(face-at-point))) ; dolor
285 (flymake-goto-next-error)
286 (should (eq 'flymake-error
(face-at-point))) ; prognata
287 (should-error (flymake-goto-next-error nil nil t
))))))
289 (ert-deftest recurrent-backend
()
290 "Test a backend that calls REPORT-FN multiple times"
294 (((symbol-function 'eager-backend
)
296 (funcall report-fn nil
:explanation
"very eager but no diagnostics")
297 (display-buffer (current-buffer))
301 (flymake-tests--diagnose-words report-fn
:warning
'("consectetur"))
306 (flymake-tests--diagnose-words report-fn
:error
'("fugiat"))
308 (insert "Lorem ipsum dolor sit amet, consectetur adipiscing
309 elit, sed do eiusmod tempor incididunt ut labore et dolore
310 manha aliqua. Ut enim ad minim veniam, quis nostrud
311 exercitation ullamco laboris nisi ut aliquip ex ea commodo
312 consequat. Duis aute irure dolor in reprehenderit in
313 voluptate velit esse cillum dolore eu fugiat nulla
314 pariatur. Excepteur sint occaecat cupidatat non prognata
315 sunt in culpa qui officia deserunt mollit anim id est
317 (let ((flymake-diagnostic-functions
318 (list 'eager-backend
))
319 (flymake-wrap-around nil
))
320 (let ((flymake-start-on-flymake-mode nil
))
323 (flymake-tests--assert-set (flymake-running-backends)
325 (cl-loop until tick repeat
4 do
(sleep-for 0.2))
327 (goto-char (point-max))
328 (flymake-goto-prev-error)
329 (should (eq 'flymake-warning
(face-at-point))) ; consectetur
330 (should-error (flymake-goto-prev-error nil nil t
))
331 (cl-loop until tick repeat
4 do
(sleep-for 0.2))
332 (flymake-goto-next-error)
333 (should (eq 'flymake-error
(face-at-point))) ; fugiat
334 (flymake-goto-prev-error)
335 (should (eq 'flymake-warning
(face-at-point))) ; back at consectetur
336 (should-error (flymake-goto-prev-error nil nil t
))
339 (ert-deftest eob-region-and-trailing-newline
()
340 "`flymake-diag-region' at eob with varying trailing newlines."
341 (cl-flet ((diag-region-substring
344 ((`(,a .
,b
) (flymake-diag-region (current-buffer) line col
)))
345 (buffer-substring a b
))))
347 (insert "beg\nmmm\nend")
349 (diag-region-substring 3 3)
352 (diag-region-substring 3 nil
)
356 (diag-region-substring 4 1)
359 (diag-region-substring 4 nil
)
363 (diag-region-substring 5 1)
366 (diag-region-substring 5 nil
)
371 (provide 'flymake-tests
)
373 ;;; flymake.el ends here